pax_global_header00006660000000000000000000000064144112364000014505gustar00rootroot0000000000000052 comment=abc7c069b91f513ecfafe562cb7b39829250d2df ledger-3.3.2/000077500000000000000000000000001441123640000127545ustar00rootroot00000000000000ledger-3.3.2/.dir-locals.el000066400000000000000000000020271441123640000154060ustar00rootroot00000000000000;;; Directory Local Variables ;;; For more information see (info "(emacs) Directory Variables") ((nil (tab-width . 2) (sentence-end-double-space . t) (bug-reference-url-format . "https://github.com/ledger/ledger/issues/%s")) (c-mode (c-file-style . "ledger") (c-style-alist ("ledger" (indent-tabs-mode) (c-basic-offset . 2) (c-comment-only-line-offset 0 . 0) (c-hanging-braces-alist (substatement-open before after) (arglist-cont-nonempty)) (c-offsets-alist (statement-block-intro . +) (knr-argdecl-intro . 5) (substatement-open . 0) (substatement-label . 0) (label . 0) (case-label . 0) (statement-case-open . 0) (statement-cont . +) (arglist-intro . +) (arglist-close . +) (inline-open . 0) (brace-list-open . 0) (topmost-intro-cont first c-lineup-topmost-intro-cont c-lineup-gnu-DEFUN-intro-cont)) (c-special-indent-hook . c-gnu-impose-minimum) (c-block-comment-prefix . "")))) (emacs-lisp-mode (indent-tabs-mode . nil))) ledger-3.3.2/.github/000077500000000000000000000000001441123640000143145ustar00rootroot00000000000000ledger-3.3.2/.github/workflows/000077500000000000000000000000001441123640000163515ustar00rootroot00000000000000ledger-3.3.2/.github/workflows/cmake.yml000066400000000000000000000057731441123640000201700ustar00rootroot00000000000000name: CMake on: push: branches: master pull_request: branches: master env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: build: runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: # os: [ubuntu-latest,macos-latest] config: - { os: ubuntu-latest, name: Ubuntu, PY_MAJOR: 3, cmake_args: -DUSE_PYTHON=ON -DUSE_GPGME=ON } - { os: macos-latest, name: MacOS, PY_MAJOR: 3, cmake_args: -DUSE_PYTHON=ON -DUSE_GPGME=ON } steps: - uses: actions/checkout@v3 name: Check out repository code - if: runner.os == 'Linux' name: Linux dependencies run: | sudo apt-get update -y sudo apt-get install libxml2-utils libgmp-dev libmpfr-dev libedit-dev libboost-dev libboost-test-dev libboost-regex-dev libboost-python-dev libboost-system-dev libboost-date-time-dev libboost-iostreams-dev libboost-filesystem-dev libboost-serialization-dev libgpgmepp-dev libgpg-error-dev libgpgme-dev - if: runner.os == 'macOS' name: Mac Dependencies run: | # Unlink and re-link to prevent errors when github mac runner images # install python outside of brew, for example: # https://github.com/orgs/Homebrew/discussions/3895 # https://github.com/actions/setup-python/issues/577 # https://github.com/actions/runner-images/issues/6459 # https://github.com/actions/runner-images/issues/6507 # https://github.com/actions/runner-images/issues/2322 brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done brew update brew install boost boost-python3 gmp mpfr gpgme - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{matrix.config.cmake_args}} -DBUILD_DEBUG=ON -DPython_FIND_VERSION_MAJOR=${{matrix.config.PY_MAJOR}} env: BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} env: BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} - name: Test working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C ${{env.BUILD_TYPE}} || ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure ledger-3.3.2/.gitignore000066400000000000000000000032001441123640000147370ustar00rootroot00000000000000*.[oa] *.so *.so.* *.dylib *.backup *.elc *.gcov *.l[oa] *.pyc *.sw[p-z] *~ .timestamp *.tar.bz2 *.tar.gz .deps/ .libs/ ABOUT-NLS Makefile Makefile.am Makefile.in TAGS acconf.h.in aclocal.m4 autogen.sh autom4te.cache/ config.guess config.h config.h.in config.log config.rpath config.status config.sub configure configure.ac depcomp doc/Doxyfile doc/*.aux doc/*.cp doc/*.cps doc/*.fn doc/*.fns doc/*.html doc/*.info doc/*.info-* doc/*.ky doc/*.kys doc/*.log doc/*.pdf doc/*.pg doc/*.toc doc/*.tp doc/*.vr doc/*.vrs doc/.dirstamp doc/html/ doc/latex/ doc/refman.pdf doc/report/ doc/version.texi install-sh intl/ ledger libtool ltmain.sh m4/ make.sh missing mkinstalldirs po/ py-compile shave shave-libtool stamp-h1 texinfo.tex tmpcvs*/ tmpwrk*/ dist/win/vc9/Debug/ dist/win/vc9/gen-mpir.exe dist/win/vc9/gen-mpir.ilk dist/win/vc9/gen-mpir.pdb dist/win/vc9/ledger.ncb dist/win/vc9/ledger.vcproj.*.user dist/win/vc9/ledger.suo dist/win/vc9/lib/Win32/Debug/ src/TAGS CMakeCache.txt CPackConfig.cmake CPackSourceConfig.cmake CMakeFiles/ CTestTestfile.cmake _CPack_Packages/ cmake_install.cmake install_manifest.txt system.hh system.hh.[gp]ch* Ledger*.dmg Ledger*.sh # Files that generated by running ./demo.sh in contrib/non-profit-audit-reports contrib/non-profit-audit-reports/tests/chart-of-accounts.txt contrib/non-profit-audit-reports/tests/general-ledger.csv contrib/non-profit-audit-reports/tests/general-ledger.ods contrib/non-profit-audit-reports/tests/general-ledger.txt contrib/non-profit-audit-reports/tests/MANIFEST contrib/non-profit-audit-reports/general-ledger.zip /wiki/ .ninja_deps .ninja_log build.ninja rules.ninja test/Testing /MathTests /UtilTests ledger-3.3.2/.travis.yml000066400000000000000000000047421441123640000150740ustar00rootroot00000000000000# NOTE: Please validate this file after editing it using # Travis WebLint https://lint.travis-ci.org/ # or travis-lint https://github.com/travis-ci/travis-lint language: cpp compiler: - gcc os: - linux - osx dist: - bionic osx_image: xcode11.2 sudo: false cache: apt: true env: global: # List of required boost libraries to build - BOOST_LIBS="date_time,filesystem,iostreams,python,regex,system,test" # Encrypted COVERITY_SCAN_TOKEN - secure: "mYNxD1B8WNSvUeKzInehZ7syi2g1jH2ymeSQxoeKKD2duq3pvNWPdZdc4o9MlWQcAqcz58rhFZRIpuEWCnP0LbbJaG+MyuemMn9uAmg9Y4gFpMsBPHuTdf8pO3rDex+tkrr9puEJFgL+QV/TehxO6NDDpx7UdYvJb+4aZD/auYI=" jobs: - PY_MAJOR=3 addons: coverity_scan: project: name: "ledger/ledger" description: "Build submitted via Travis CI" build_command_prepend: "cmake . -DUSE_PYTHON=ON -DBUILD_DEBUG=ON -DUSE_GPGME=ON -DCLANG_GCOV=ON -DPython_FIND_VERSION_MAJOR=${PY_MAJOR}" build_command: "make" branch_pattern: coverity apt: packages: - libgmp-dev - libmpfr-dev - libedit-dev - libboost-dev - libboost-test-dev - libboost-regex-dev - libboost-python-dev - libboost-system-dev - libboost-date-time-dev - libboost-iostreams-dev - libboost-filesystem-dev - libboost-serialization-dev - libgpgmepp-dev - libgpg-error-dev - libgpgme-dev homebrew: update: true packages: - boost - boost-python3 - gmp - mpfr - gpgme before_script: # On macOS boost-python packaging is broken - if [ "$TRAVIS_OS_NAME" = osx ]; then EXTRA_CMAKE_ARGS="-DBoost_NO_BOOST_CMAKE=ON"; fi # Ensure cmake locates python 3.8. Brew changed boost-python3 to use 3.8 but it isn't in the path by default - if [ "$TRAVIS_OS_NAME" = osx ]; then export PATH="/usr/local/opt/python@3.8/bin:$PATH"; fi - cmake . -DUSE_PYTHON=ON -DPython_FIND_VERSION_MAJOR=${PY_MAJOR} -DUSE_GPGME=ON -DBUILD_DEBUG=ON $EXTRA_CMAKE_ARGS - make VERBOSE=1 script: - ctest --output-on-failure notifications: email: on_success: change on_failure: change irc: channels: [ "irc.libera.chat#ledger" ] on_success: change on_failure: change webhooks: urls: - https://webhooks.gitter.im/e/0050d91909a8cde39e35 on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: never # options: [always|never|change] default: always ledger-3.3.2/.yamllint000066400000000000000000000001121441123640000146000ustar00rootroot00000000000000extends: default rules: document-start: disable line-length: disable ledger-3.3.2/CMakeLists.txt000066400000000000000000000236601441123640000155230ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) PROJECT(ledger) set(Ledger_VERSION_MAJOR 3) set(Ledger_VERSION_MINOR 3) set(Ledger_VERSION_PATCH 2) set(Ledger_VERSION_PRERELEASE "") set(Ledger_VERSION_DATE 20230330) set(Ledger_TEST_TIMEZONE "America/Chicago") # Point CMake at any custom modules we may ship list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") if ((${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.7.0") AND (${CMAKE_VERSION} VERSION_LESS "3.16.0")) # use backported module from 3.15 (introduced 3.12) to support older versions of cmake. # this only works with cmake 3.7 or higher. list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/python-backport") endif() enable_testing() add_definitions( -std=c++11 -DBOOST_FILESYSTEM_NO_DEPRECATED ) if (CYGWIN) add_definitions(-U__STRICT_ANSI__) endif() ######################################################################## option(USE_PYTHON "Build support for the Python scripting bridge" OFF) option(USE_DOXYGEN "Build reference documentation using Doxygen" OFF) option(DISABLE_ASSERTS "Build without any internal consistency checks" OFF) option(BUILD_DEBUG "Build support for runtime debugging" OFF) option(PRECOMPILE_SYSTEM_HH "Precompile system.hh" ON) option(USE_GPGME "Build with support for encrypted journals" OFF) option(BUILD_LIBRARY "Build and install Ledger as a library" ON) option(BUILD_DOCS "Build and install documentation" OFF) option(BUILD_WEB_DOCS "Build version of documentation suitable for viewing online" OFF) if (BUILD_DEBUG) set(CMAKE_BUILD_TYPE Debug) set(DEBUG_MODE 1) else() set(CMAKE_BUILD_TYPE Release) set(DEBUG_MODE 0) endif() if (DISABLE_ASSERTS) set(NO_ASSERTS 1) else() set(NO_ASSERTS 0) endif() if (CLANG_GCOV) set(PROFILE_LIBS profile_rt) set(CMAKE_REQUIRED_LIBRARIES ${PROFILE_LIBS}) endif() ######################################################################## find_package(Python COMPONENTS Interpreter) # Used for running tests if (USE_PYTHON) if (NOT BUILD_LIBRARY) message(ERROR "Building the python module requires BUILD_LIBRARY=ON.") endif() find_package(Python COMPONENTS Interpreter Development) if (PYTHON_FOUND) if (${Python_VERSION_MAJOR} VERSION_GREATER_EQUAL "3") set(BOOST_PYTHON "python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}") set(HAVE_BOOST_PYTHON 1) include_directories(SYSTEM ${Python_INCLUDE_DIRS}) else() set(HAVE_BOOST_PYTHON 0) message("Ledger requires Python >= 3.x") endif() else() set(HAVE_BOOST_PYTHON 0) message("Could not find a Python library to use with Boost.Python") endif() else() set(HAVE_BOOST_PYTHON 0) endif() # Set BOOST_ROOT to help CMake to find the right Boost version find_package(Boost 1.49.0 REQUIRED date_time filesystem system iostreams regex unit_test_framework ${BOOST_PYTHON} OPTIONAL_COMPONENTS nowide) # enable Boost::nowide library (for UTF8 command line args on Windows) set(HAVE_BOOST_NOWIDE 0) if (Boost_NOWIDE_FOUND) set(HAVE_BOOST_NOWIDE 1) endif() include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) # Crypto if (USE_GPGME) find_package(Gpgmepp REQUIRED) set(HAVE_GPGME 1) include_directories(SYSTEM ${Gpgmepp_INCLUDE_DIRS}) link_directories(${Gpgmepp_LIBRARY_DIRS}) else() set(HAVE_GPGME 0) endif() ######################################################################## include(CheckIncludeFiles) include(CheckLibraryExists) include(CheckFunctionExists) include(CheckCSourceCompiles) include(CheckCXXSourceCompiles) include(CheckCXXSourceRuns) include(CMakePushCheckState) check_function_exists(getpwuid HAVE_GETPWUID) check_function_exists(getpwnam HAVE_GETPWNAM) check_function_exists(ioctl HAVE_IOCTL) check_function_exists(isatty HAVE_ISATTY) check_c_source_compiles(" #include #include #include #include #include #include int main() { int status, pfd[2]; status = pipe(pfd); status = fork(); if (status < 0) { ; } else if (status == 0) { char *arg0 = NULL; status = dup2(pfd[0], STDIN_FILENO); close(pfd[1]); close(pfd[0]); execlp(\"\", arg0, (char *)0); perror(\"execl\"); exit(1); } else { close(pfd[0]); } return 0; }" UNIX_PIPES_COMPILES) if (UNIX_PIPES_COMPILES) set(HAVE_UNIX_PIPES 1) else() set(HAVE_UNIX_PIPES 0) endif() cmake_push_check_state() set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH} ${Boost_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Boost_LIBRARIES} icuuc ${PROFILE_LIBS}) check_cxx_source_runs(" #include using namespace boost; int main() { std::string text = \"Активы\"; u32regex r = make_u32regex(\"активы\", regex::perl | regex::icase); return u32regex_search(text, r) ? 0 : 1; }" BOOST_REGEX_UNICODE_RUNS) if (BOOST_REGEX_UNICODE_RUNS) set(HAVE_BOOST_REGEX_UNICODE 1) else() set(HAVE_BOOST_REGEX_UNICODE 0) endif() cmake_pop_check_state() # Check if fix for https://github.com/boostorg/python/issues/39 is needed if (HAVE_BOOST_PYTHON) cmake_push_check_state() set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH} ${Boost_INCLUDE_DIRS} ${Python_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Boost_LIBRARIES} ${Python_LIBRARIES} ${PROFILE_LIBS}) check_cxx_source_compiles(" #include struct X { int y; }; int main() { boost::python::make_setter(&X::y); return 0; }" BOOST_MAKE_SETTER_COMPILES) if (BOOST_MAKE_SETTER_COMPILES) set(HAVE_BOOST_159_ISSUE_39 0) else() set(HAVE_BOOST_159_ISSUE_39 1) endif() cmake_pop_check_state() endif() ######################################################################## include_directories(${CMAKE_INCLUDE_PATH}) macro(find_opt_library_and_header _header_var _header _lib_var _lib _have_var) if (${_have_var}) find_path(${_header_var} ${_header}) if (NOT ${_header_var}) set(${_have_var} 0) else() find_library(${_lib_var} ${_lib}) if (NOT ${_lib_var}) set(${_have_var} 0) else() include_directories(SYSTEM "${${_header_var}}") set(${_have_var} 1) endif() endif() else() set(${_have_var} 0) endif() endmacro(find_opt_library_and_header _header_var _header _lib_var _lib _have_var) macro(find_req_library_and_header _header_var _header _lib_var _lib) find_path(${_header_var} ${_header}) if (NOT ${_header_var}) message(SEND_ERROR "Could not find ${_header} on your system") else() include_directories(SYSTEM "${${_header_var}}") find_library(${_lib_var} ${_lib}) if (NOT ${_lib_var}) message(SEND_ERROR "Could not find library ${_lib} on your system") endif() endif() endmacro(find_req_library_and_header _header_var _header _lib_var _lib) find_req_library_and_header(GMP_PATH gmp.h GMP_LIB gmp) find_req_library_and_header(MPFR_PATH mpfr.h MPFR_LIB mpfr) check_library_exists(edit readline "" HAVE_EDIT) find_opt_library_and_header(EDIT_PATH histedit.h EDIT_LIB edit HAVE_EDIT) #find_package(Gettext) # Used for running tests #if (GETTEXT_FOUND) # set(HAVE_GETTEXT 1) #else() set(HAVE_GETTEXT 0) #endif() #find_path(INTL_PATH libintl.h) #find_library(INTL_LIB intl) #include_directories(SYSTEM "${INTL_PATH}") ######################################################################## macro(add_ledger_library_dependencies _target) target_link_libraries(${_target} ${MPFR_LIB}) target_link_libraries(${_target} ${GMP_LIB}) if (HAVE_EDIT) target_link_libraries(${_target} ${EDIT_LIB}) endif() if (HAVE_GETTEXT) target_link_libraries(${_target} ${INTL_LIB}) endif() if (HAVE_GPGME) target_link_libraries(${_target} Gpgmepp) endif() if (HAVE_BOOST_PYTHON) if (CMAKE_SYSTEM_NAME STREQUAL Darwin) # Don't link directly to a Python framework on macOS, to avoid segfaults # when the module is imported from a different interpreter target_link_libraries(${_target} ${Boost_LIBRARIES}) set_target_properties(${_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") else() target_link_libraries(${_target} ${Boost_LIBRARIES} ${Python_LIBRARIES}) endif() else() target_link_libraries(${_target} ${Boost_LIBRARIES}) endif() if (HAVE_BOOST_REGEX_UNICODE) target_link_libraries(${_target} icuuc) endif() target_link_libraries(${_target} ${PROFILE_LIBS}) endmacro(add_ledger_library_dependencies _target) ######################################################################## include(FindUtfcpp) if (UTFCPP_FOUND) include_directories("${UTFCPP_INCLUDE_DIR}") else() message(FATAL_ERROR "Missing required header file: utf8.h\n" "Define UTFCPP_PATH or install utfcpp locally into the source tree below lib/utfcpp/." ) endif() set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) # add the binary tree to the search path for include files so that we will # find system.hh include_directories("${PROJECT_BINARY_DIR}") configure_file( ${PROJECT_SOURCE_DIR}/src/system.hh.in ${PROJECT_BINARY_DIR}/system.hh) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem ") endif() add_subdirectory(src) add_subdirectory(doc) add_subdirectory(test) ######################################################################## # build a CPack driven installer package include (InstallRequiredSystemLibraries) set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.md") set (CPACK_PACKAGE_VERSION_MAJOR "${Ledger_VERSION_MAJOR}") set (CPACK_PACKAGE_VERSION_MINOR "${Ledger_VERSION_MINOR}") set (CPACK_PACKAGE_VERSION_PATCH "${Ledger_VERSION_PATCH}${Ledger_VERSION_PRERELEASE}") set (CPACK_GENERATOR "TBZ2") set (CPACK_SOURCE_GENERATOR "TBZ2") set (CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") set (CPACK_SOURCE_IGNORE_FILES "/.git*;/.dir-locals.el;~$;/doc/website/;/doc/wiki/;/lib/*.sh;/lib/Makefile;/tools/;${CPACK_SOURCE_IGNORE_FILES}") include (CPack) ### CMakeLists.txt ends here ledger-3.3.2/CONTRIBUTING.md000066400000000000000000000106521441123640000152110ustar00rootroot00000000000000Tips for contributors --------------------- * Please **make pull requests against `master`**. * Please add a **test case** under `test/regress` when possible. * If you're making **changes to files for which the CI build is not relevant**, please **add `[ci skip]` to the end of the commit message**. * Report bugs using [GitHub Issues]. GLOSSARY ---- Developing the Ledger software uses a number different tools, not all of which will be familiar to all developers. **[Boost]**: a standard set of C++ libraries. Most Boost libraries consist of inline functions and templates in header files. **[Boost.Python]**: C++ library which enables seamless interoperability between C++ and the Python programming language. **[CMake]**: A cross platform system for building from source code. It uses the `CMakeLists.txt` files. **[Doxygen]**: generates programming documentation from source code files. Primarily used on C++ sources, but works on all. Uses the `doc/Doxyfile.in` file. **[GCC]**: Gnu Compiler Collection, which includes the *gcc* compiler and *gcov* coverage/profiler tool. **[clang]**: C language family frontend for LLVM, which includes the *clang* compiler. **[GMP]**: Gnu Multiple Precision Arithmetic Library provides arbitrary precision math. **[MPFR]**: Gnu Multiple Precision Floating-point Library with correct rounding. **[Markdown]**: A typesetter format that produces *html* files from *.md* files. Note that GitHub automatically renders *.md* files. **[SHA1]**: a marginally secure cryptographic hash function, used only for signing the license file. **[Texinfo]**: Gnu documentation typesetter that produces *html* and *pdf* files from the `doc/\*.texi` files. **[GitHub Actions]**: a hosted continuous integration service that builds and runs tests each commit posted to GitHub. Each build creates a log, updates a [small badge] at the top left of the main project's [README.md], and emails the author of the commit if any tests fail. **[utfcpp]**: a library for handling utf-8 in a variety of C++ versions. Orientation --- The source tree can be confusing to a new developer. Here is a selective orientation: **./acprep**: a custom thousand-line script to install dependencies, grab updates, and build. It also creates `\*.cmake`, `./CmakeFiles/` and other CMake temporary files. Use `./acprep --help` for more information. **./README.md**: user readme file in markdown format, also used as the project description on GitHub. **./contrib/**: contributed scripts of random quality and completion. They usually require editing to run. **./doc/**: documentation, licenses, and tools for generating documents such as the *pdf* manual. **./lib/**: a couple of libraries used in development. **./python/**: samples using the Python ledger module. **./src/**: the C++ header and source files in a flat directory. **./test/**: a testing harness with subdirectories full of tests **./tools/**: an accretion of tools, mostly small scripts, to aid development Building --- If you are going to be working on Ledger, you'll want to enable both debug builds (which are the default, using `acprep`), and also the use of pre-compiled headers. To do this, specify your compiler as either `clang++` or `g++` as follows: mkdir build ./acprep --compiler=clang++ cd build make This will set up a debug build using clang++ (and pre-compiled headers, which is enabled by the combination of those two), and then start a build. For even quicker rebuilds, try the Ninja build tool, which is very fast at determining what to rebuild, and automatically takes advantage of multiple cores: mkdir build ./acprep --compiler=clang++ --ninja cd build ninja [Boost]: http://boost.org [Boost.Python]: http://www.boost.org/libs/python/ [GitHub Issues]: https://github.com/ledger/ledger/issues [GMP]: http://gmplib.org/ [MPFR]: http://www.mpfr.org/ [CMake]: http://www.cmake.org [Doxygen]: http://doxygen.org [Markdown]: https://daringfireball.net/projects/markdown/ [SHA1]: http://en.wikipedia.org/wiki/SHA-1 [Texinfo]: http://www.gnu.org/software/texinfo/ [GitHub Actions]: https://github.com/features/actions [GCC]: http://gcc.gnu.org [utfcpp]: http://utfcpp.sourceforge.net [small badge]: https://github.com/ledger/ledger/actions/workflows/cmake.yml/badge.svg [git-flow]: http://nvie.com/posts/a-successful-git-branching-model/ [README.md]: https://github.com/ledger/ledger/blob/master/README.md [clang]: http://clang.llvm.org ledger-3.3.2/INSTALL.md000066400000000000000000000154201441123640000144060ustar00rootroot00000000000000# INSTALL To build this code after doing a Git clone, run: $ ./acprep dependencies $ ./acprep update If anything goes wrong, see "COMMON CONFIGURE/BUILD PROBLEMS" below. If you try to configure and build without running acprep first, you are almost certainly going to run into problems. In future, you can run `acprep update` again and again, and it will keep you updated to the very latest version. Now install it: $ sudo make install ## COMMON CONFIGURE / BUILD PROBLEMS To build and install Ledger requires several dependencies on various platforms. You can install these dependencies very simply for most of them using: $ ./acprep dependencies The first order of business if acprep update doesn't work is to find out where things went wrong. So follow these steps to produce a bug report I can track down easily: $ ./acprep --debug update # shows what acprep was thinking $ $EDITOR CMakeCache.txt # shows what cmake was thinking With the contents of config.log, and the output from acprep --debug update, it's usually fairly obvious where things have gone astray. ## F.A.Q. Q: The build fails saying it can't find `utf8.h` A: You didn't run `./acprep update`. ---------------------------------------------------------------------- Q: `./acprep update` gives errors or `./acprep dependencies` fails A: You're probably missing some dependency libraries. If you tried `./acprep dependencies` already and that didn't solve the problem, then you may need to install dependencies by hand. On a Debian GNU/Linux system (or Debian-based system such as Ubuntu), something like this should work (as root): $ sudo apt-get install build-essential cmake texinfo python-dev \ zlib1g-dev libbz2-dev libgmp3-dev gettext libmpfr-dev \ libboost-date-time-dev libboost-filesystem-dev \ libboost-graph-dev libboost-iostreams-dev \ libboost-python-dev libboost-regex-dev libboost-test-dev \ doxygen libedit-dev libmpc-dev tzdata ---------------------------------------------------------------------- Q: Configure fails saying it can't find boost_regex A: Look in config.log and search for "boost_regex", then scroll down a bit until you see the exact compile error. Usually it's failing because your include directory is different from anything acprep is expecting to see. It could also be failing because your Boost libraries have a custom "suffix" on them. Let's say your Boost was installed in ~/boost, and every library has the suffix `-xgcc42`. This is what you would run: $ CPPFLAGS=-I$HOME/boost acprep --boost=xgcc42 update ---------------------------------------------------------------------- Q: Configure fails saying it can't find MPFR A: You need MPFR version 2.4.0 or higher. This version does not come with most Debian distributions, so you will need to build it. The relevant packages are `libmpfr-dev` and `libmpfr-dbg`. See also the question above about what to do if `./acprep update` gives errors or `./acprep dependencies` fails. ---------------------------------------------------------------------- Q: I'm seeing a segfault deep inside the boost_regex code! A: Actually, the real segfault is in libstdc++'s facet code. It's being caused by using a debug Boost with a non-debug build of Ledger, or vice-versa. ---------------------------------------------------------------------- Q: Something else fails, or Ledger crashes on startup A: This, I am most interested in hearing about. Please file a bug at the Ledger Issue Tracker, https://github.com/ledger/ledger/issues. The more details you can provide, the better. Also, if Ledger is crashing, try running it under gdb like so: $ gdb ledger (gdb) run ... runs till crash ... (gdb) bt Put that backtrace output, and the output from `ledger --version` in the bug report. ---------------------------------------------------------------------- Q: Whenever I try to use the Python support, I get a segfault A: Make sure that the boost_python library you linked against is using the exact same Python as the Ledger executable. In particular I see this bug on macOS systems where boost_python is linked against the default Python, while Ledger is linked against the version provided by MacPorts. Or vice versa. Solution: Use one or the other. If you prefer the system Python, run `port deactivate -f python26`, to get MacPorts' version out of the way. You'll then need to delete the Ledger binary and run `make` to relink it. ---------------------------------------------------------------------- Q: When I run `make check`, the Python unit tests always crash A: This can happen for the same reason as above. It can also happen if you have ICU support enabled. This is a bug I'm still trying to track down. ---------------------------------------------------------------------- Q: My distribution has versions of Boost and/or CMake that are too old for Ledger. How do I build my own Boost and/or CMake binaries that will work properly with Ledger? Thereafter, how do I configure Ledger properly to use those newly built versions of Boost and/or CMake? A: Here's commands that one user used to make this work, for Boost 1.51.0 on Debian GNU/Linux 6.0.x (aka Debian squeeze). It's likely to work ok for other versions of Boost as well. YMMV on other distributions and/or other Debian distribution versions, though. - Preparing and building Boost $ export BOOST_VERSION=1.57.0 $ cd /somewhere/you/want/to/build/boost $ wget -N http://iweb.dl.sourceforge.net/project/boost/boost/$BOOST_VERSION/boost_${BOOST_VERSION//./_}.tar.bz2 $ tar xvf boost_${BOOST_VERSION//./_}.tar.bz2 $ cd boost_${BOOST_VERSION//./_} $ ./bootstrap.sh $ ./b2 --build-type=complete --layout=tagged --prefix=/where/you/want/boost/installed $ ./b2 --build-type=complete --layout=tagged --prefix=/where/you/want/boost/installed install - Preparing and building CMake $ export CMAKE_VERSION=3.1.0 $ cd /somewhere/you/want/to/build/cmake $ wget -N http://www.cmake.org/files/v${CMAKE_VERSION:0:3}/cmake-${CMAKE_VERSION}.tar.gz $ tar xvf cmake-${CMAKE_VERSION}.tar.gz $ cd cmake-${CMAKE_VERSION} $ ./configure --prefix=/where/you/want/cmake/installed/ $ make $ make install - Building Ledger using the CMake and/or Boost as installed above $ cd /path/to/ledger/sources $ env PATH=/where/you/want/cmake/installed/bin:$PATH BOOST_ROOT=/where/you/want/boost/installed PREFIX=/where/you/want/ledger/installed $SHELL $ ./acprep --prefix=$PREFIX --debug --python config $ ./acprep --prefix=$PREFIX --debug --python make $ ./acprep --prefix=$PREFIX --debug --python install ledger-3.3.2/LICENSE.md000066400000000000000000000027241441123640000143650ustar00rootroot00000000000000Copyright (c) 2003-2023, John Wiegley. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of New Artisans LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ledger-3.3.2/NEWS.md000066400000000000000000001217671441123640000140700ustar00rootroot00000000000000# Ledger NEWS ## 3.3.2 (2023-03-30) - Fix divide by zero (bugs #777 and #2207) - Increase string size limit in src/unistring.h assert (bug #2174) - Require tzdata for Nix flake build (bug #2213) ## 3.3.1 (2023-03-03) - Fix regression leading to incorrect error about `format` directives (bug #2205) - Add information about compile features to `--version` - Fix compiler warnings by minimizing the use of deprecated APIs - Update flake.nix to match nixpkgs ledger/default.nix - Remove unused Python server related code - Various documentation improvements ## 3.3 (2023-02-08) - Use `$PAGER` when environment variable is set (bug #1674) - Make `--depth` correctly fold postings to accounts of greater depth into the parent at the specified level (bug #987) - When using wild-cards in the `include` directive, include matched files in sorted order (bug #1659) - Ensure absolute path for include (bug #2075) - Try to use `$XDG_HOME_CONFIG/ledger/ledgerrc` or `~/.config/ledger/ledgerrc` first - Improve Python 3 support and drop support for Python 2 - Add support for automatically reading files encrypted with GPG (bug #1949) - Add support for a "debit" column in the `convert` command (bug #1120) - Fix parsing of files without end of line (bug #516) - Fix incorrect parsing of expressions containing a `-` without spaces (bug #2001) - Fix payee metadata on postings not being validated and payee aliases not being honored (bug #556 & bug #1892) - Fix ledger interpreting a posting with 0 difference as a null-posting, which leads to it auto-balancing the posting (bug #1942) - Correctly escape all string values in lisp report (bug #2034) - Fix a regression where empty commodities were shown (bug #1969) - Fix a regression where using multiple commodities in one transaction triggers an assertion (bug #1998) - Fix --time-colon for negative time amounts - Use correct int return type for stream input operations (bug #2058) - Use amount_width for balance report - Remove some UTF-8 code that was having no effect (bug #2061) - Fix unrounding for equity - Fix SIGABRT when python subcommand raises an exception - Improve XML reports - Support building on older versions of CMAKE (less than 3.7) - Fix compilation with Boost 1.76 (bug #2030) - Fix Msys2 MinGW build (bug #1905) - Fix unicode problems on Windows (bug #1986) - Fix the issue that with Boost >= 1.77 `include` directive cannot find the file to include for stdin (`-f -`). Also for `-f -` when `include` cannot find the file it reports the error with full path now. (bug #2057 & bug #2092) - Fix Nix build - Rename `quoted_rfc4180` to `quoted_rfc`, as numbers used in function names confuses the parser (#2007). - Numbers are no longer permitted in value expression function names. - Various documentation improvements ## 3.2.1 (2020-05-18) - Fix regression with expression evaluation by reverting commit `Correction to the way parens are parsed in query expressions` (bug #1894) - Fix --invert breakage by reverting commit `Change --invert to invert displayed amounts and totals, not amounts` (bug #1895) - Fix performance regression by reverting commit `Compare price annotations using their textual rendering` (bug #1907) - Fix library path issue (bug #1885) - Allow specifying the Python version (bug #1893) - Some documentation fixes ## 3.2.0 (2020-05-01) - Port Python support to Python 3 - Entities are no longer regarded as defined due to being part of a cleared transaction. `--explicit` is effectively enabled by default and is now a no-op (PR #1819) - Add `--average-lot-prices` to show the average of lot prices - Add support for `%F` date format specifier (bug #1775) - Add `commodity_price(NAME, DATE)` function - Add `set_commodity_price(NAME, DATE)` function - Fix buffer overflow when evaluating date - Fix balance assertions on accounts with virtual posts (bug #543) - Fix segfault with `ledger print` (bug #1850) - Ensure that `apply` directives (like `apply account`) have the required argument (bug #553) - Format annotations using a date format that can be parsed - Change `--invert` to invert displayed amounts and totals, not amounts (bug #1803) - Correct the way parens are parsed in query expressions - Compare price annotations using their textual rendering - Fix build failure with utfcpp 3.0 (bug #1816) - Fix build failure due to ambiguous type (bug #1833) ## 3.1.3 (2019-03-31) - Properly reject postings with a comment right after the flag (bug #1753) - Make sorting order of lot information deterministic (bug #1747) - Fix bug in tag value parsing (bug #1702) - Remove the `org` command, which was always a hack to begin with (bug #1706) - Provide Docker information in README - Various small documentation improvements ## 3.1.2 (2019-02-05) - Increase maximum length for regex from 255 to 4095 (bug #981) - Initialize periods from from/since clause rather than earliest transaction date (bug #1159) - Check balance assertions against the amount after the posting (bug #1147) - Allow balance assertions with multiple posts to same account (bug #1187) - Fix period duration of "every X days" and similar statements (bug #370) - Make option `--force-color` not require `--color` anymore (bug #1109) - Add `quoted_rfc4180` to allow CVS output with RFC 4180 compliant quoting. - Add support for `--prepend-format` in accounts command - Fix handling of edge cases in trim function (bug #520) - Fix auto xact posts not getting applied to account total during journal parse (bug #552) - Transfer `null_post` flags to generated postings - Fix segfault when using `--market` with `--group-by` - Use `amount_width` variable for budget report - Keep pending items in budgets until the last day they apply - Fix bug where `.total` used in value expressions breaks totals - Make automated transactions work with assertions (bug #1127) - Improve parsing of date tokens (bug #1626) - Don't attempt to invert a value if it's already zero (bug #1703) - Do not parse user-specified init-file twice - Fix parsing issue of effective dates (bug #1722, TALOS-2017-0303, CVE-2017-2807) - Fix use-after-free issue with deferred postings (bug #1723, TALOS-2017-0304, CVE-2017-2808) - Fix possible stack overflow in option parsing routine (bug #1222, CVE-2017-12481) - Fix possible stack overflow in date parsing routine (bug #1224, CVE-2017-12482) - Fix use-after-free when using `--gain` (bug #541) - Python: Removed double quotes from Unicode values. - Python: Ensure that parse errors produce useful `RuntimeErrors` - Python: Expose `journal expand_aliases` - Python: Expose `journal_t::register_account` - Improve bash completion - Emacs Lisp files have been moved to https://github.com/ledger/ledger-mode - Fix build under MSYS (32-bit). - Fix build under Cygwin. - Various documentation improvements ## 3.1.1 (2016-01-11) - Added a `--no-revalued` option - Improved Embedded Python Support - Use `./.ledgerrc` if `~/.ledgerrc` doesn't exist - Fixed parsing of transactions with single-character payees and comments - Fixed crash when using `-M` with empty result - Fixed sorting for option `--auto-match` - Fixed treatment of `year 2015` and `Y2014` directives - Fixed crash when using `--trace` 10 or above - Build fix for boost 1.58, 1.59, 1.60 - Build fix for Cygwin - Fixed Util and Math tests on Mac OS X - Various documentation improvements - Examples in the documentation are tested just like unit tests - Add continuous integration (https://travis-ci.org/ledger/ledger) ## 3.1 (2014-10-05) - Changed the definition of cost basis to preserve the original cost basis when a gain or loss is made (if you bought 1 AAA for $10 and then sold it for $12, ledger would previously take $12 as the cost; the original cost of $10 is preserved as the cost basis now, which addresses strange behavior with -B after a capital gain or loss is made). - Incorrect automatic Equity:Capital Gains and Equity:Capital Loss entries are no longer generated when a commodity is sold for loss or profit. - Support for virtual posting costs. - The option `--permissive` now quiets balance assertions - Removed SHA1 files due to license issues and use boost instead. - Added option `--no-pager` to disable the pager. - Added option `--no-aliases` to completely disable alias expansion - Added option `--recursive-aliases` to expand aliases recursively - Support payee `uuid` directive. - Bug fix: when a status flag (`!` or `*`) is explicitly specified for an individual posting, it always has a priority over entire transaction status. - Bug fix: don't lose commodity when cost is not separated by whitespace - Improved backwards compatibility with ledger 2.x - Build fix for GCC 4.9 - Build fix for boost 1.56 - Many improvements to ledger-mode, including fontification - More test cases and unit tests - Contrib: Added script to generate commodities from ISO 4217 ## 3.0 Due to the magnitude of changes in 3.0, only changes that affect compatibility with 2.x files and usage is mentioned here. For a description of new features, please see the manual. - The option `-g` (`--performance`) was removed. - The balance report now defaults to showing all relevant accounts. This is the opposite of 2.x. That is, `bal` in 3.0 does what `-s bal` did in 2.x. To see 2.6 behavior, use `bal -n` in 3.0. The `-s` option no longer has any effect on balance reports. ## 2.6.3 - Minor fixes to allow for compilation with gcc 4.4. ## 2.6.2 - Bug fix: Command-line options, such as -O, now override init-file options such as -V. - Bug fix: "cat data | ledger -f -" now works. - Bug fix: --no-cache is now honored. Previously, it was writing out a cache file named "". - Bug fix: Using %.2X in a format string now outputs 2 spaces if the state is cleared. ## 2.6.1 - Added the concept of "balance setting transactions": Setting an account's balance You can now manually set an account's balance to whatever you want, at any time. Here's how it might look at the beginning of your Ledger file: 2008/07/27 Starting fresh Assets:Checking = $1,000.00 Equity:Opening Balances If Assets:Checking is empty, this is no different from omitting the "=". However, if Assets:Checking did have a prior balance, the amount of the transaction will be auto-calculated so that the final balance of Assets:Checking is now $1,000.00. Let me give an example of this. Say you have this: 2008/07/27 Starting fresh Assets:Checking $750.00 Equity:Opening Balances 2008/07/27 Starting fresh Assets:Checking = $1,000.00 Equity:Adjustments These two entries are exactly equivalent to these two: 2008/07/27 Starting fresh Assets:Checking $750.00 Equity:Opening Balances 2008/07/27 Starting fresh Assets:Checking $250.00 Equity:Adjustments The use of the "=" sign here is that it sets the transaction's amount to whatever is required to satisfy the assignment. This is the behavior if the transaction's amount is left empty. # Multiple commodities As far as commodities go, the = sign only works if the account balance's commodity matches the commodity of the amount after the equals sign. However, if the account has multiple commodities, only the matching commodity is affected. Here's what I mean: 2008/07/24 Opening Balance Assets:Checking = $250.00 ; we force set it Equity:Opening Balances 2008/07/24 Opening Balance Assets:Checking = EC 250.00 ; we force set it again Equity:Opening Balances This is an error, because $250.00 cannot be auto-balanced to match EC 250.00. However: 2008/07/24 Opening Balance Assets:Checking = $250.00 ; we force set it again Assets:Checking EC 100.00 ; and add some EC's Equity:Opening Balances 2008/07/24 Opening Balance Assets:Checking = EC 250.00 ; we force set the EC's Equity:Opening Balances This is *not* an error, because the latter auto-balancing transaction only affects the EC 100.00 part of the account's balance; the $250.00 part is left alone. Checking statement balances When you reconcile a statement, there are typically one or more transactions which result in a known balance. Here's how you specify that in your Ledger data: 2008/07/24 Opening Balance Assets:Checking = $100.00 Equity:Opening Balances 2008/07/30 We spend money, with a known balance afterward Expenses:Food $20.00 Assets:Checking = $80.00 2008/07/30 Again we spend money, but this time with all the info Expenses:Food $20.00 Assets:Checking $-20.00 = $60.00 2008/07/30 This entry yield an 'unbalanced' error Expenses:Food $20.00 Assets:Checking $-20.00 = $30.00 The last entry in this set fails to balance with an unbalanced remainder of $-10.00. Either the entry must be corrected, or you can have Ledger deal with the remainder automatically: 2008/07/30 The fixed entry Expenses:Food $20.00 Assets:Checking $-20.00 = $30.00 Equity:Adjustments Conclusion This simple feature has all the utility of @check, plus auto-balancing to match known target balances, plus the ability to guarantee that an account which uses only one commodity does contain only that commodity. This feature slows down textual parsing slightly, does not affect speed when loading from the binary cache. - The rest of the changes in the version is all bug fixes (around 45 of them). ## 2.6.0.90 - Gnucash parser is fixed. - Fix a memory leak bug in the amount parser. - (This feature is from 2.6, but was not documented anywhere): Commodities may now specify lot details, to assign in managing set groups of items, like buying and selling shares of stock. For example, let's say you buy 50 shares of AAPL at $10 a share: 2007/01/14 Stock purchase Assets:Brokerage 50 AAPL @ $10 Assets:Brokerage Three months later, you sell this "lot". Based on the original purchase information, Ledger remembers how much each share was purchased for, and the date on which it was purchased. This means you can sell this specific lot by price, by date, or by both. Let's sell it by price, this time for $20 a share. 2007/04/14 Stock purchase Assets:Brokerage $1000.00 Assets:Brokerage -50 AAPL {$10} @ $20 Income:Capital Gains $-500.00 Note that the Income:Capital Gains line is now required to balance the transaction. Because you sold 50 AAPL at $20/share, and because you are selling shares that were originally valued at $10/share, Ledger needs to know how you will "balance" this difference. An equivalent Expenses:Capital Loss would be needed if the selling price were less than the buying price. Here's the same example, this time selling by date and price: 2007/04/14 Stock purchase Assets:Brokerage $1000.00 Assets:Brokerage -50 AAPL {$10} [2007/01/14] @ $20 Income:Capital Gains $-500.00 If you attempt to sell shares for a date you did not buy them, note that Ledger will not complain (as it never complains about the movement of commodities between accounts). In this case, it will simply create a negative balance for such shares within your Brokerage account; it's up to you to determine whether you have them or not. - To facilitate lot pricing reports, there are some new reporting options: * --lot-prices Report commodities with different lot prices as if they were different commodities. Otherwise, Ledger just gloms all the AAPL shares together. * --lot-dates Separate commodities by lot date. Every transaction that uses the '@' cost specifier will have an implicit lot date and lot price. * --lot-tags Separate commodities by their arbitrary note tag. Note tags may be specified using (note) after the commodity. * --lots Separate commodities using all lot information. ## 2.6 - The style for eliding long account names (for example, in the register report) has been changed. Previously Ledger would elide the end of long names, replacing the excess length with "..". However, in some cases this caused the base account name to be missing from the report! What Ledger now does is that if an account name is too long, it will start abbreviating the first parts of the account name down to two letters in length. If this results in a string that is still too long, the front will be elided -- not the end. For example: Expenses:Cash ; OK, not too long Ex:Wednesday:Cash ; "Expenses" was abbreviated to fit Ex:We:Afternoon:Cash ; "Expenses" and "Wednesday" abbreviated ; Expenses:Wednesday:Afternoon:Lunch:Snack:Candy:Chocolate:Cash ..:Af:Lu:Sn:Ca:Ch:Cash ; Abbreviated and elided! As you can see, it now takes a very deep account name before any elision will occur, whereas in 2.x elisions were fairly common. - In addition to the new elision change mentioned above, the style is also configurable: * --truncate leading ; elide at the beginning * --truncate middle ; elide in the middle * --truncate trailing ; elide at end (Ledger 2.x's behavior) * --truncate abbrev ; the new behavior * --abbrev-len 2 ; set length of abbreviations These elision styles affect all format strings which have a maximum width, so they will also affect the payee in a register report, for example. In the case of non-account names, "abbrev" is equivalent to "trailing", even though it elides at the beginning for long account names. - Error reporting has been greatly improving, now showing full contextual information for most error messages. - Added --base reporting option, for reporting convertible commodities in their most basic form. For example, if you read a timeclock file with Ledger, the time values are reported as hour and minutes -- whichever is the most compact form. But with --base, Ledger reports only in seconds. NOTE: Setting up convertible commodities is easy; here's how to use Ledger for tracking quantities of data, where the most compact form is reported (unless --base is specified): C 1.00 Kb = 1024 b C 1.00 Mb = 1024 Kb C 1.00 Gb = 1024 Mb C 1.00 Tb = 1024 Gb - Added --ansi reporting option, which shows negative values in the running total column of the register report as red, using ANSI terminal codes; --ansi-invert makes non-negative values red (which makes more sense for the income and budget reports). The --ansi functionality is triggered by the format modifier "!", for example the register reports uses the following for the total (last) column: %!12.80T At the moment neither the balance report nor any of the other reports make use of the ! modifier, and so will not change color even if --ansi is used. However, you can modify these report format strings yourself in ~/.ledgerrc if you wish to see red coloring of negative sums in other places. - Added --only predicate, which occurs during transaction processing between --limit and --display. Here is a summary of how the three supported predicates are used: * --limit "a>100" This flag limits computation to *only transactions whose amount is greater than 100 of a given commodity*. It means that if you scan your dining expenses, for example, only individual bills greater than $100 would be calculated by the report. * --only "a>100" This flag happens much later than --limit, and corresponding more directly to what one normally expects. If --limit isn't used, then ALL your dining expenses contribute to the report, *but only those calculated transactions whose value is greater than $100 are used*. This becomes important when doing a monthly costs report, for example, because it makes the following command possible: ledger -M --only "a>100" reg ^Expenses:Food This shows only *months* whose amount is greater than 100. If --limit had been used, it would have been a monthly summary of all individual dinner bills greater than 100 -- which is a very different thing. * --display "a>100" This predicate does not constrain calculation, but only display. Consider the same command as above: ledger -M --display "a>100" reg ^Expenses:Food This displays only lines whose amount is greater than 100, *yet the running total still includes amounts from all transactions*. This command has more particular application, such as showing the current month's checking register while still giving a correct ending balance: ledger --display "d>[this month]" reg Checking Note that these predicates can be combined. Here is a report that considers only food bills whose individual cost is greater than $20, but shows the monthly total only if it is greater than $500. Finally, we only display the months of the last year, but we retain an accurate running total with respect to the entire ledger file: ledger -M --limit "a>20" --only "a>200" \ --display "year == yearof([last year])" reg ^Expenses:Food - Added new "--descend AMOUNT" and "--descend-if VALEXPR" reporting options. For any reports that display valued transactions (i.e., register, print, etc), you can now descend into the component transactions that made up any of the values you see. For example, say you request a --monthly expenses report: $ ledger --monthly register ^Expenses Now, in one of the reported months you see $500.00 spent on Expenses:Food. You can ask Ledger to "descend" into, and show the component transactions of, that $500.00 by respecifying the query with the --descend option: $ ledger --monthly --descend "\$500.00" register ^Expenses The --descend-if option has the same effect, but takes a value expression which is evaluated as a boolean to locate the desired reported transaction. - Added a "dump" command for creating binary files, which load much faster than their textual originals. For example: ledger -f huge.dat -o huge.cache dump ledger -f huge.cache bal The second command will load significantly faster (usually about six times on my machine). - There have a few changes to value expression syntax. The most significant incompatibilities being: * Equality is now ==, not = * The U, A, and S functions now requires parens around the argument. Whereas before At was acceptable, now it must be specified as A(t). * The P function now always requires two arguments. The old one-argument version P(x) is now the same as P(x,m). The following value expression features are new: * A C-like comma operator is supported, where all but the last term are ignored. The is significant for the next feature: * Function definitions are now supported. Scoping is governed by parentheses. For example: (x=100, x+10) ; yields 110 as the result (f(x)=x*2,f(100)) ; yields 200 as the result * Identifier names may be any length. Along with this support comes alternate, longer names for all of the current one-letter value expression variables: Old New --- --- m now a amount a amount b cost i price d date X cleared Y pending R real L actual n index N count l depth O total B cost_total I price_total v market V market_total g gain G gain_total U(x) abs(x) S(x) quant(x), quantity(x) comm(x), commodity(x) setcomm(x,y), set_commodity(x,y) A(x) mean(x), avg(x), average(x) P(x,y) val(x,y), value(x,y) min(x,y) max(x,y) - There are new "parse" and "expr" commands, whose argument is a single value expression. Ledger will simply print out the result of evaluating it. "parse" happens before parsing your ledger file, while "expr" happens afterward. Although "expr" is slower as a result, any commodities you use will be formatted based on patterns of usage seen in your ledger file. These commands can be used to test value expressions, or for doing calculation of commoditized amounts from a script. A new "--debug" will also dump the resulting parse tree, useful for submitting bug reports. - Added new min(x,y) and max(x,y) value expression functions. - Value expression function may now be defined within your ledger file (or initialization file) using the following syntax: @def foo(x)=x*1000 This line makes the function "foo" available to all subsequent value expressions, to all command-line options taking a value expression, and to the new "expr" command (see above). ## 2.5 - Added a new value expression regexp command: C// compare against a transaction amount's commodity symbol - Added a new "csv" command, for outputting results in CSV format. - Ledger now expands ~ in file pathnames specified in environment variables, initialization files and journal files. - Effective dates may now be specified for entries: 2004/10/03=2004/09/30 Credit card company Liabilities:MasterCard $100.00 Assets:Checking This entry says that although the actual transactions occurred on October 3rd, their effective date was September 30th. This is especially useful for budgeting, in case you want the transactions to show up in September instead of October. To report using effective dates, use the --effective option. - Actual and effective dates may now be specified for individual transactions: 2004/10/03=2004/09/30 Credit card company Liabilities:MasterCard $100.00 Assets:Checking ; [2004/10/10=2004/09/15] This states that although the actual date of the entry is 2004/10/03, and the effective date of the entry is 2004/09/30, the actual date of the Checking transaction itself is 2004/10/10, and its effective date is 2004/09/15. The effective date is optional (just specifying the actual date would have read "[2004/10/10]"). If no effective date is given for a transaction, the effective date of the entry is assumed. If no actual date is given, the actual date of the entry is assumed. The syntax of the latter is simply [=2004/09/15]. - To support the above, there is a new formatting option: "%d". This outputs only the date (like "%D") if there is no effective date, but outputs "ADATE=EDATE" if there is one. The "print" report now uses this. - To support the above, the register report may now split up entries whose component transactions have different dates. For example, given the following entry: 2005/10/15=2005/09/01 iTunes Expenses:Music $1.08 ; [2005/10/20=2005/08/01] Liabilities:MasterCard The command "ledger register" on this data file reports: 2005/10/20 iTunes Expenses:Music $1.08 $1.08 2005/10/15 iTunes Liabilities:MasterCard $-1.08 0 While the command "ledger --effective register" reports: 2005/08/01 iTunes Expenses:Music $1.08 $1.08 2005/09/01 iTunes Liabilities:MasterCard $-1.08 0 Although it appears as though two entries are being reported, both transactions belong to the same entry. - Individual transactions may now be cleared separately. The old syntax, which is still supported, clears all transactions in an entry: 2004/05/27 * Book Store Expenses:Dining $20.00 Liabilities:MasterCard The new syntax allows clearing of just the MasterCard transaction: 2004/05/27 Book Store Expenses:Dining $20.00 * Liabilities:MasterCard NOTE: This changes the output format of both the "emacs" and "xml" reports. ledger.el uses the new syntax unless the Lisp variable `ledger-clear-whole-entries' is set to t. - Removed Python integration support. - Did much internal restructuring to allow the use of libledger.so in non-command-line environments (such as GUI tools). ## 2.4.1 - Corrected an issue that had inadvertently disabled Gnucash support. ## 2.4 - Both `-$100.00` and `$-100.00` are now equivalent amounts. - Simple, inline math (using the operators +-/*, and/or parentheses) is supported in transactions. For example: 2004/05/27 Book Store Expenses:Dining $20.00 + $2.50 Liabilities:MasterCard This won't register the tax/tip in its own account, but might make later reading of the ledger file easier. - Use of a "catch all" account is now possible, which auto-balances entries that contain _only one transaction_. For sanity's sake this is not used to balance all entries, as that would make locating unbalanced entries a nightmare. Example: A Liabilities:MasterCard 2004/05/27 Book Store Expenses:Dining $20.00 + $2.50 This is equivalent to the entry in the previous bullet. - Entries that contain a single transaction with no amount now always balance, even if multiple commodities are involved. This means that the following is now supported, which wasn't previously: 2004/06/21 Adjustment Retirement 100 FUNDA Retirement 200 FUNDB Retirement 300 FUNDC Equity:Adjustments - Fixed several bugs relating to QIF parsing, budgeting and forecasting. - The configure process now looks for libexpat in addition to searching for libxmlparse+libxmltok (how expat used to be packaged). ## 2.3 - The directive "!alias ALIAS = ACCOUNT" makes it possible to use "ALIAS" as an alternative name for ACCOUNT in a textual ledger file. You might use this to associate the single word "Bank" with the checking account you use most, for example. - The --version page shows the optional modules ledger was built with. - Fixed several minor problems, plus a few major ones dealing with imprecise date parsing. ## 2.2 - Ledger now compiles under gcc 2.95. - Fixed several core engine bugs, and problems with Ledger's XML data format. - Errors in XML or Gnucash data now report the correct line number for the error, instead of always showing line 1. - 'configure' has been changed to always use a combination of both compile and link tests for every feature, in order to identify environment problems right away. - The "D " command, released in 2.1, now requires a commoditized amount, such as "D $1,000.00". This sets not only the default commodity, but several flags to be used with all such commodities (such as whether numbering should be American or European by default). This entry may be used be many times; the most recent seen specifies the default for entries that follow. - The binary cache now remembers the price history database that was used, so that if LEDGER_PRICE_DB is silently changed, the cache will be thrown away and rebuilt. - OFX data importing is now supported, using libofx (http://libofx.sourceforge.net). configure will check if the library is available. You may need to add CPPFLAGS or LDFLAGS to the command-line for the appropriate headers and library to be found. This support is preliminary, and as such is not documented yet. - All journal entries now remember where they were read from. New format codes to access this information are: %S for source path, %B for beginning character position, and %E for ending character position. - Added "pricesdb" command, which is identical to "prices" except that it uses the same format as Ledger's usual price history database. - Added "output FILE" command, which attempts to reproduce the input journal FILE exactly. Meant for future GUI usage. This command relies on --write-hdr-format and --write-xact-format, instead of --print-format. - Added "--reconcile BALANCE" option, which attempts to reconcile all matching transactions to the given BALANCE, outputting those that would need to be "cleared" to match it. Using by the auto-reconciling feature of ledger.el (see below). "--reconcile-date DATE" ignores any uncleared transactions after DATE in the reconciling algorithm. Since the algorithm is O(n^2) (where 'n' is the number of uncleared transactions to consider), this could have a substantial impact. - In ledger.el's *Reconcile* mode ('C-c C-r' from a ledger-mode file): * 'a' adds a missing transaction * 'd' deletes the current transaction * 'r' attempts to auto-reconcile (same as 'C-u C-c C-r') * 's' or 'C-x C-s' will save the ledger data file and show the currently cleared balance * 'C-c C-c' commits the pending transactions, marking them cleared. This feature now works with Emacs 21.3. Also, the reconciler no longer needs to ask "how far back" to go. - To support the reconciler, textual entries may now have a "!" flag (pending) after the date, instead of a "*" flag (cleared). - There are a new set of value expression regexp commands: * c// entry code * p// payee * w// short account name * W// full account name * e// transaction note This makes it possible to display transactions whose comment field matches a particular text string. For example: ledger -l e/{tax}/ reg prints out all the transactions with the comment "{tax}", which might be used to identify items related to a tax report. ## 2.1 - Improved the autoconf system to be smarter about finding XML libs - Added --no-cache option, to always ignore any binary cache file - `ledger-reconcile' (in ledger.el) no longer asks for a number of days - Fixed %.XY format, where X is shorter than the string generated by Y - New directive for text files: "D " specifies the default commodity used by the entry command ## 2.0 This version represents a full rewrite, while preserving much of the original data format and command-line syntax. There are too many new features to describe in full, but a quick list: value expressions, complex date masks, binary caching of ledger data, several new reporting options, a simple way to specify payee regexps, calculation and display predicates, and two-way Python integration. Ledger also uses autoconf now, and builds as a library in addition to a command-line driver. ### Differences from 1.7 - changes in option syntax: -d now specifies the display predicate. To give a date mask similar to 1.7, use the -p (period) option. -P now generates the "by payee" report. To specify a price database to use, use --price-db. -G now generates a net gain report. To print totals in a format consumable by gnuplot, use -J. -l now specifies the calculation predicate. To emulate the old usage of "-l \$100", use: -d "AT>100". -N is gone. Instead of "-N REGEX", use: -d "/REGEX/?T>0:T". -F now specifies the report format string. The old meaning of -F now has little use. -S now takes a value expression as the sorting criterion. To get the old meaning of "-S", use "-S d". -n now means "collapse entries in the register report". The get the old meaning of -n in the balance report, use "-T a". -p now specifies the reporting period. You can convert commodities in a report using value expressions. For example, to display hours at $10 per hour: -T "O>={0.01h}?{\$10.00}*O:O" Or, to reduce totals, so that every $417 becomes 1.0 AU: -T "O>={\$0.01}?{1.0 AU}*(O/{\$417}):O" - The use of "+" and "-" in ledger files to specify permanent regexps has been removed. - The "-from" argument is no longer used by the "entry" command. Simply remove it. ### Features new to 2.0 - The most significant feature to be added is "value expressions". They are used in many places to indicate what to display, sorting order, how to calculate totals, etc. Logic and math operators are supported, as well as simple functions. See the manual. - If the environment variable LEDGER_FILE (or LEDGER) is used, a binary cache of that ledger is kept in ~/.ledger-cache (or the file given by LEDGER_CACHE). This greatly speeds up subsequent queries. Happens only if "-f" or "--file" is not used. - New 'xml' report outputs an XML version of what "register" would have displayed. This can be used to manipulate reported data in a more scriptable way. Ledger can also read as input the output from the "xml" report. If the "xml" report did not contain balanced entries, they will be balanced by the "" account. For example: ledger reg rent displays the same results as: ledger xml rent | ledger -f - reg rent - Regexps given directly after the command name now apply only to account names. To match on a payee, use "--" to separate the two kinds of regexps. For example, to find a payee named "John" within all Expenses accounts, use: ledger register expenses -- john Note: This command is identical (and internally converted) to: ledger -l "/expenses/|//john/" register - To include entries from another file into a specific account, use: !account ACCOUNT !include FILE !end - Register reports now show only matching account transactions. Use "-r" to see "related accounts" -- the account the transfer came from or went to (This was the old behavior in 1.x, but led to confusion). "-r" also works with balance reports, where it will total all the transactions related to your query. - Automated transactions now use value expressions for the predicate. The new syntax is: = VALUE-EXPR TRANSACTIONS... Only one VALUE-EXPR is supported (compared to multiple account regexps before). However, since value expression allow for logic chaining, there is no loss of functionality. Matching can also be much more comprehensive. - If Boost.Python is installed (libboost_python.a), ledger can support two-way Python integration. This feature is enabled by passing --enable-python to the "configure" script before building. Ledger can then be used as a module (ledger.so), as well as supporting Python function calls directly from value expressions. See main.py for an example of driving Ledger from Python. It implements nearly all the functionality of the C++ driver, main.cc. (This feature has yet to mature, and so is being offered as a beta feature in this release. It is mostly functional, and those curious are welcome to play with it.) - New reporting options: * "-o FILE" outputs data to FILE. If "-", output goes to stdout (the default). * -O shows base commodity values (this is the old behavior) * -B shows basis cost of commodities * -V shows market value of commodities * -g reports gain/loss performance of each register item * -G reports net gain/loss over time * -A reports average transaction value (arithmetic mean) * -D reports each transaction's deviation from the average * -w uses 132 columns for the register report, rather than 80. Set the environment variable LEDGER_WIDE for this to be the default. * "-p INTERVAL" allows for more flexible period reporting, such as: monthly every week every 3 quarters weekly from 12/20 monthly in 2003 weekly from last month until dec * "-y DATEFMT" changes the date format used in all reports. The default is "%Y/%m/%d". -Y and -W print yearly and weekly subtotals, just as -M prints monthly subtotals. * --dow shows cumulative totals for each day of the week. * -P reports transactions grouped by payee * -x reports the payee as the commodity; useful in some cases * -j and -J replace the previous -G (gnuplot) option. -j reports the amounts column in a way gnuplot can consume, and -J the totals column. An example is in "scripts/report". * "--period-sort EXPR" sorts transactions within a reporting period. The regular -S option sorts all reported transactions. ## 1.7 - Pricing histories are now supported, so that ledger remembers the historical prices of all commodities, and can present register reports based on past and present market values as well as original cost basis. See the manual for more details on the new option switches. ## 1.6 - Ledger can now parse timeclock files. These are simple timelogs that track in/out events, which can be maintained using my timeclock tool. By allowing ledger to parse these, it means that reporting can be done on them in the same way as ledger files (the commodity used is "h", for hours); it means that doing things like tracking billable hours for clients, and invoicing those clients to transfer hours into dollar values via a receivable account, is now trivial. See the docs for more on how to do this. - Began keeping a NEWS file. :) ledger-3.3.2/README.md000066400000000000000000000200641441123640000142350ustar00rootroot00000000000000[![Join the chat at https://gitter.im/use-package/Lobby](https://badges.gitter.im/use-package/Lobby.svg)](https://gitter.im/use-package/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ![Build Status master](https://github.com/ledger/ledger/actions/workflows/cmake.yml/badge.svg) [![Status](https://img.shields.io/badge/status-active-brightgreen.svg?style=flat)](https://github.com/ledger/ledger/pulse/monthly) [![License](https://img.shields.io/badge/license-BSD-blue.svg?style=flat)](https://opensource.org/licenses/BSD-3-Clause) [![GitHub release](https://img.shields.io/github/release/ledger/ledger.svg?style=flat)](https://github.com/ledger/ledger/releases) # Ledger: Command-Line Accounting Ledger is a powerful, double-entry accounting system that is accessed from the UNIX command-line. This may put off some users, since there is no flashy UI, but for those who want unparalleled reporting access to their data there are few alternatives. Ledger uses text files for input. It reads the files and generates reports; there is no other database or stored state. To use Ledger, you create a file of your account names and transactions, run from the command line with some options to specify input and requested reports, and get output. The output is generally plain text, though you could generate a graph or html instead. Ledger is simple in concept, surprisingly rich in ability, and easy to use. ## For the Impatient I know, you just want to build and play. If you have all the [dependencies](#dependencies) installed, then simply do this: $ git clone git@github.com:ledger/ledger.git $ cd ledger && ./acprep update # Update to the latest, configure, make Now try your first ledger command: $ ./ledger -f test/input/sample.dat reg For help on keeping your journal have a look at the [documentation] and the [wiki][] (Also see the “Resources” section at the end of this file). An Emacs mode for Ledger files can be found in the [ledger/ledger-mode repository] and a vim plugin is located in the [ledger/vim-ledger repository]. ## Docker version If you have Docker installed on your computer or server, you can use a [Docker version](https://hub.docker.com/r/dcycle/ledger/) of this software, without installing any further dependencies: $ docker run --rm -v "$PWD"/test/input:/data dcycle/ledger:1 -f /data/sample.dat reg ## Dependencies If you wish to proceed in this venture, you'll need a few dependencies. The easiest way to get them for your platform is to run this handy Python script: $ ./acprep dependencies Note that some features, e.g. `--import` require building Ledger with Python support. If that doesn't completely work, here are the dependencies for building the current `master` branch: Dependency | Version (or greater) ------------|--------------------- [Boost] | 1.49 [GMP] | 4.2.2 [MPFR] | 2.4.0 [utfcpp] | 2.3.4 [gettext] | 0.17 _optional_ [libedit] | 20090111-3.0 _optional_ [Python] | 3.9 _optional_ [doxygen] | 1.5.7.1 _optional_, for `make docs` [graphviz] | 2.20.3 _optional_, for `make docs` [texinfo] | 4.13 _optional_, for `make docs` [lcov] | 1.6 _optional_, for `make report`, used with `/./acprep gcov` [sloccount] | 2.26 _optional_, for `make sloc` ### macOS You can use [Homebrew] or [MacPorts] to install build dependencies for Ledger easily on macOS. #### 1. Homebrew If you use Homebrew, to install the dependencies you would run: $ brew install cmake boost boost-python3 gmp mpfr #### 2. MacPorts If you build stuff using MacPorts on macOS, as I do, here is what you would run: $ sudo port install -f cmake python37 \ libiconv zlib gmp \ mpfr ncurses ncursesw \ gettext libedit boost-jam \ boost +st+python37+icu texlive doxygen graphviz \ texinfo lcov sloccount ### Conda The dependencies for building Ledger are available from [conda-forge] on certain platforms (for example, `linux-64`), which can be used with [Conda] or [mamba]. With Conda you could run: $ conda install -c conda-forge python=3 cmake boost gmp mpfr \ gettext libedit texinfo doxygen graphviz ### Ubuntu If you're going to build on Ubuntu, `sudo apt-get install ...` the following packages (current as of Ubuntu 18.04): $ sudo apt-get install build-essential cmake doxygen \ libboost-system-dev libboost-dev python3-dev gettext git \ libboost-date-time-dev libboost-filesystem-dev \ libboost-iostreams-dev libboost-python-dev libboost-regex-dev \ libboost-test-dev libedit-dev libgmp3-dev libmpfr-dev texinfo tzdata ### Debian Debian 10 (bullseye), Debian 11 ("bullseye"), Debian testing and Debian unstable (sid) contain all components needed to build ledger. You can install all required build dependencies using the following command: $ sudo apt-get install build-essential cmake autopoint texinfo python3-dev \ zlib1g-dev libbz2-dev libgmp3-dev gettext libmpfr-dev \ libboost-date-time-dev libboost-filesystem-dev \ libboost-graph-dev libboost-iostreams-dev \ libboost-python-dev libboost-regex-dev libboost-test-dev ### Fedora You can install all required build dependencies under Fedora using the following command (tested with Fedora 32): $ sudo dnf install boost-date-time boost-devel boost-filesystem \ boost-iostreams boost-python3-devel boost-regex boost-system \ boost-test cmake doxygen gettext git gmp-devel libedit-devel \ mpfr-devel python3-devel texinfo tzdata ## Building The next step is preparing your environment for building. While you can use `cmake .` and make, I've prepared a script that does a lot more of the footwork for you: $ ./acprep update # or, if you want to use the Boost libraries with suffix -mt, install in # $HOME/local and build with 2 processes in parallel $ ./acprep update --boost-suffix=-mt --prefix=$HOME/local -j2 Please read the contents of `CMakeFiles/CMakeOutput.log` and `CMakeFiles/CMakeError.log` if the configure step fails. Also, see the `help` subcommand to `acprep`, which explains some of its many options. It's pretty much the only command I run for configuring, building and testing Ledger. You can run `make check` to confirm the result, and `make install` to install. ## Resources Now that you're up and running, here are a few resources to keep in mind: - [Homepage] - [Documentation] - [IRC channel][IRC]: #ledger channel on Libera Chat - [Mailing List / Forum][mailing list] - [GitHub project page][github] - [Code analysis][openhub] If you have ideas you'd like to share, the best way is either to e-mail me a patch (I prefer attachments over pasted text), or to get an account on GitHub. Once you do, fork the [Ledger project][github], hack as much as you like, then send me a pull request via GitHub. [Homepage]: https://ledger-cli.org/ [documentation]: https://www.ledger-cli.org/docs.html [mailing list]: https://list.ledger-cli.org/ [wiki]: https://wiki.ledger-cli.org/ [IRC]: irc://irc.libera.chat/ledger [github]: https://github.com/ledger/ledger [ledger/vim-ledger repository]: https://github.com/ledger/vim-ledger [Homebrew]: https://brew.sh/ [MacPorts]: https://www.macports.org/ [Boost]: https://boost.org [GMP]: https://gmplib.org/ [MPFR]: https://www.mpfr.org/ [utfcpp]: https://utfcpp.sourceforge.net [gettext]: https://www.gnu.org/software/gettext/ [libedit]: https://thrysoee.dk/editline/ [Python]: https://python.org [doxygen]: https://www.doxygen.org/ [graphviz]: https://graphviz.org/ [texinfo]: https://www.gnu.org/software/texinfo/ [lcov]: https://ltp.sourceforge.net/coverage/lcov.php [sloccount]: https://www.dwheeler.com/sloccount/ [pcre]: https://www.pcre.org/ [libofx]: https://libofx.sourceforge.net [expat]: https://libexpat.github.io [libxml2]: http://xmlsoft.org [openhub]: https://www.openhub.net/p/ledger [conda-forge]: https://conda-forge.org [Conda]: https://conda.io [mamba]: https://github.com/mamba-org/mamba ledger-3.3.2/acprep000077500000000000000000001334731441123640000141670ustar00rootroot00000000000000#!/usr/bin/env python # acprep, version 3.1 # # This script simply sets up the compiler and linker flags for all the various # build permutations I use for testing and profiling. import inspect import logging import logging.handlers import optparse import os import re import shutil import sys import locale try: import hashlib except: import md5 from os.path import * from stat import * from subprocess import Popen, PIPE, call LEVELS = {'DEBUG': logging.DEBUG, 'INFO': logging.INFO, 'WARNING': logging.WARNING, 'ERROR': logging.ERROR, 'CRITICAL': logging.CRITICAL} def which(program): def is_exe(fpath): return os.path.exists(fpath) and os.access(fpath, os.X_OK) def ext_candidates(fpath): yield fpath for ext in os.environ.get("PATHEXT", "").split(os.pathsep): yield fpath + ext fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) for candidate in ext_candidates(exe_file): if is_exe(candidate): return candidate return None class BoostInfo(object): def dependencies(self, system): if system in ['darwin-homebrew']: return [ 'boost' ] if system in ['darwin-macports']: return [ 'boost-jam', 'boost', '+python37' ] if system in ['centos']: return [ 'boost-devel' ] elif system in ['ubuntu-focal', 'ubuntu-bionic', 'ubuntu-xenial', 'ubuntu-eoan', 'ubuntu-trusty', 'ubuntu-cosmic']: return [ 'libboost-dev', 'libboost-date-time-dev', 'libboost-filesystem-dev', 'libboost-iostreams-dev', 'libboost-python-dev', 'libboost-regex-dev', 'libboost-system-dev', 'libboost-test-dev', 'tzdata' ] elif system in [ 'ubuntu-saucy', 'ubuntu-precise']: return [ 'autopoint', 'libboost-dev', 'libboost-test-dev', 'libboost-regex-dev', 'libboost-date-time-dev', 'libboost-filesystem-dev', 'libboost-iostreams-dev', 'libboost-python-dev' ] elif system in ['ubuntu-lucid']: return [ 'bjam', 'autopoint', 'libboost-dev', 'libboost-regex-dev', 'libboost-date-time-dev', 'libboost-filesystem-dev', 'libboost-iostreams-dev', 'libboost-python-dev' ] class CommandLineApp(object): "Base class for building command line applications." force_exit = True # If true, always ends run() with sys.exit() log_handler = None boost_major = "1_52" def __init__(self): "Initialize CommandLineApp." # Create the logger self.log = logging.getLogger(os.path.basename(sys.argv[0])) ch = logging.StreamHandler() formatter = logging.Formatter("%(name)s: %(levelname)s: %(message)s") ch.setFormatter(formatter) self.log.addHandler(ch) self.log_handler = ch # Setup the options parser usage = 'usage: %prog [OPTIONS...] [ARGS...]' op = self.option_parser = optparse.OptionParser(usage = usage, conflict_handler = 'resolve') op.add_option('', '--debug', action='store_true', dest='debug', default=False, help='show debug messages and pass exceptions') op.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='show informational messages') op.add_option('-q', '--quiet', action='store_true', dest='quiet', default=False, help='do not show log messages on console') op.add_option('', '--log', metavar='FILE', type='string', action='store', dest='logfile', default=False, help='append logging data to FILE') op.add_option('', '--loglevel', metavar='LEVEL', type='string', action='store', dest='loglevel', default=False, help='set log level: DEBUG, INFO, WARNING, ERROR, CRITICAL') self.options = op.get_default_values() def main(self, *args): """Main body of your application. This is the main portion of the app, and is run after all of the arguments are processed. Override this method to implement the primary processing section of your application.""" pass def handleInterrupt(self): """Called when the program is interrupted via Control-C or SIGINT. Returns exit code.""" self.log.error('Canceled by user.') return 1 def handleMainException(self): "Invoked when there is an error in the main() method." if not self.options.debug: self.log.exception('Caught exception') return 1 ## INTERNALS (Subclasses should not need to override these methods) def run(self): """Entry point. Process options and execute callback functions as needed. This method should not need to be overridden, if the main() method is defined.""" # Process the options supported and given self.options, main_args = self.option_parser.parse_args(values=self.options) if self.options.logfile: fh = logging.handlers.RotatingFileHandler(self.options.logfile, maxBytes = (1024 * 1024), backupCount = 5) formatter = logging.Formatter("%(asctime)s - %(levelname)s: %(message)s") fh.setFormatter(formatter) self.log.addHandler(fh) if self.options.quiet: self.log.removeHandler(self.log_handler) ch = logging.handlers.SysLogHandler() formatter = logging.Formatter("%(name)s: %(levelname)s: %(message)s") ch.setFormatter(formatter) self.log.addHandler(ch) self.log_handler = ch if self.options.loglevel: self.log.setLevel(LEVELS[self.options.loglevel]) elif self.options.debug: self.log.setLevel(logging.DEBUG) elif self.options.verbose: self.log.setLevel(logging.INFO) exit_code = 0 try: # We could just call main() and catch a TypeError, but that would # not let us differentiate between application errors and a case # where the user has not passed us enough arguments. So, we check # the argument count ourself. argspec = inspect.getfullargspec(self.main) expected_arg_count = len(argspec[0]) - 1 if len(main_args) >= expected_arg_count: exit_code = self.main(*main_args) else: self.log.debug('Incorrect argument count (expected %d, got %d)' % (expected_arg_count, len(main_args))) self.option_parser.print_help() exit_code = 1 except KeyboardInterrupt: exit_code = self.handleInterrupt() except SystemExit as msg: exit_code = msg.args[0] except Exception: exit_code = self.handleMainException() if self.options.debug: raise if self.force_exit: sys.exit(exit_code) return exit_code class PrepareBuild(CommandLineApp): ######################################################################### # Initialization routines # ######################################################################### def initialize(self): self.log.debug('Initializing all state variables') self.should_clean = False self.configured = False self.current_ver = None #self.current_flavor = 'default' self.current_flavor = 'debug' self.products_dir = None self.configure_args = [] self.CXXFLAGS = [] self.LDFLAGS = [] self.envvars = { 'CXX': '', 'CXXFLAGS': '', 'LDFLAGS': '', } for varname in self.envvars.keys(): if varname in os.environ: self.envvars[varname] = os.environ[varname] if varname.endswith('FLAGS'): self.__dict__[varname] = str.split(os.environ[varname]) self.envvars[varname] = '' # If ~/Products/ or build/ exists, use them instead of the source tree # for building products = self.default_products_directory() if (exists(products) and isdir(products)) or \ (exists('build') and isdir('build')): self.options.build_dir = None def __init__(self): CommandLineApp.__init__(self) self.log.setLevel(logging.INFO) self.source_dir = os.getcwd() op = self.option_parser op.add_option('', '--help', action="callback", callback=self.option_help, help='Show this help text') op.add_option('-j', '--jobs', metavar='N', type='int', action='store', dest='jobs', default=1, help='Allow N make jobs at once') op.add_option('', '--boost', metavar='BOOST_ROOT', action="store", dest="boost_root", help='Set Boost library root (ex: "--boost=/usr/local")') op.add_option('', '--boost-suffix', metavar='BOOST_SUFFIX', action="store", dest="boost_suffix", help='Set Boost library suffix (ex: "--boost-suffix=-mt")') op.add_option('', '--boost-include', metavar='BOOST_INCLUDE', action="store", dest="boost_include", help='Set Boost include path (ex: "--boost-include=DIR")') op.add_option('', '--compiler', metavar='COMPILER', action="store", dest="compiler", help='Use the Clang C++ compiler') op.add_option('', '--cxx', metavar='COMPILER', action="store", dest="compiler", help='Use the Clang C++ compiler') op.add_option('-N', '--ninja', action='store_true', dest='use_ninja', default=False, help='Use ninja to build, rather than make') op.add_option('', '--no-git', action='store_true', dest='no_git', default=False, help='Do not call out to Git; useful for offline builds') op.add_option('', '--doxygen', action='store_true', dest='enable_doxygen', default=False, help='Enable use of Doxygen to build ref manual ("make docs")') op.add_option('', '--python', action='store_true', dest='python', default=False, help='Enable Python support') op.add_option('', '--no-python', action='store_false', dest='python', help='Disable python support (default)') op.add_option('', '--prefix', metavar='DIR', action="store", dest="prefix_dir", help='Use custom installation prefix') op.add_option('', '--products', metavar='DIR', action="store", dest="option_products", help='Collect all build products in this directory') op.add_option('', '--output', metavar='DIR', action="store", default=self.source_dir, dest="build_dir", help='Build in the specified directory') op.add_option('', '--local', action="callback", callback=self.option_local, help='Build directly within the source tree (default)') self.options = op.get_default_values() self.initialize() def main(self, *args): if args and args[0] in ['default', 'debug', 'opt', 'gcov', 'gprof']: self.current_flavor = args[0] args = args[1:] if args: cmd = args[0] if 'phase_' + cmd not in PrepareBuild.__dict__: self.log.error("Unknown build phase: " + cmd + "\n") sys.exit(1) else: args = args[1:] else: cmd = 'config' self.log.info('Invoking primary phase: ' + cmd) PrepareBuild.__dict__['phase_' + cmd](self, *args) ######################################################################### # General utility code # ######################################################################### def execute(self, *args): try: self.log.debug('Executing command: ' + ' '.join(args)) retcode = call(args, shell=False) if retcode < 0: self.log.error("Child was terminated by signal", -retcode) sys.exit(1) elif retcode != 0: self.log.error("Execution failed: " + ' '.join(args)) sys.exit(1) except OSError as e: self.log.error("Execution failed: " + e) sys.exit(1) def get_stdout(self, *args): try: self.log.debug('Executing command: ' + ' '.join(args)) proc = Popen(args, shell=False, stdout=PIPE) stdout = proc.stdout.read() retcode = proc.wait() if retcode < 0: self.log.error("Child was terminated by signal", -retcode) sys.exit(1) elif retcode != 0: self.log.error("Execution failed: " + ' '.join(args)) sys.exit(1) return stdout[:-1] except OSError as e: self.log.error("Execution failed:" + e) sys.exit(1) def isnewer(self, file1, file2): "Check if file1 is newer than file2." if not exists(file2): return True return os.stat(file1)[ST_MTIME] > os.stat(file2)[ST_MTIME] ######################################################################### # Determine information about the surroundings # ######################################################################### def prefix_directory(self): if self.options.prefix_dir: return self.options.prefix_dir else: return None def default_products_directory(self): return join(os.environ['HOME'], "Products") def products_directory(self): if not self.products_dir: products = self.default_products_directory() if not exists(products) or not isdir(products): products = join(self.source_dir, 'build') products = join(products, basename(self.source_dir)) self.products_dir = products return self.products_dir def build_directory(self): if not self.options.build_dir: self.options.build_dir = join(self.products_directory(), self.current_flavor) return self.options.build_dir def ensure(self, dirname): if not exists(dirname): self.log.info('Making directory: ' + dirname) os.makedirs(dirname) elif not isdir(dirname): self.log.error('Directory is not a directory: ' + dirname) sys.exit(1) return dirname def git_working_tree(self): return exists('.git') and isdir('.git') and not self.options.no_git def current_version(self): if not self.current_ver: major, minor, patch, date = None, None, None, None version_m4 = open('CMakeLists.txt', 'r') for line in version_m4.readlines(): match = re.match('^set\(Ledger_VERSION_MAJOR ([0-9]+)\)', line) if match: major = match.group(1) match = re.match('^set\(Ledger_VERSION_MINOR ([0-9]+)\)', line) if match: minor = match.group(1) match = re.match('^set\(Ledger_VERSION_PATCH ([0-9]+)\)', line) if match: patch = match.group(1) match = re.match('^set\(Ledger_VERSION_DATE ([0-9]+)\)', line) if match: date = match.group(1) break self.current_ver = "%s.%s.%s%s" % (major, minor, patch, "-%s" % date if date else "") version_m4.close() return self.current_ver def phase_products(self, *args): self.log.info('Executing phase: products') print(self.products_directory()) def phase_info(self, *args): self.log.info('Executing phase: info') environ, conf_args = self.configure_environment() self.log.info("Current version => " + self.current_version()) self.log.info("Current flavor => " + self.current_flavor) self.log.info("Source directory => " + self.source_dir) if self.prefix_directory(): self.log.info("Installation prefix => " + self.prefix_directory()) self.log.info("Products directory => " + self.products_directory()) self.log.info("Build directory => " + self.build_directory()) self.log.debug('CMake environment =>') keys = environ.keys() for key in sorted(keys): if key in ['PATH', 'CXX'] or key.endswith('FLAGS'): self.log.debug(' %s=%s' % (key, environ[key])) self.log.debug('CMake arguments =>') for arg in conf_args + list(args): self.log.debug(' %s' % arg) def phase_sloc(self, *args): self.log.info('Executing phase: sloc') self.execute('sloccount', 'src', 'python', 'test') ######################################################################### # Update local files with the latest information # ######################################################################### def phase_pull(self, *args): self.log.info('Executing phase: pull') if self.git_working_tree(): self.execute('git', 'pull') ######################################################################### # Automatic installation of build dependencies # ######################################################################### def phase_dependencies(self, *args): self.log.info('Executing phase: dependencies') self.log.info("Installing Ledger's build dependencies ...") system = self.get_stdout('uname', '-s').decode(locale.getpreferredencoding()) if system == 'Darwin': if exists('/opt/local/bin/port'): self.log.info('Looks like you are using MacPorts on macOS') packages = [ 'sudo', 'port', 'install', '-f', 'automake', 'autoconf', 'libtool', 'python37', 'libiconv', 'zlib', 'gmp', 'mpfr', 'ncurses', 'gettext', 'libedit', 'texlive-xetex', 'doxygen', 'graphviz', 'texinfo', 'lcov', 'sloccount' ] + BoostInfo().dependencies('darwin-macports') self.log.info('Executing: ' + ' '.join(packages)) self.execute(*packages) elif exists('/usr/local/bin/brew') or exists('/opt/local/bin/brew'): self.log.info('Looks like you are using Homebrew on macOS') packages = [ 'brew', 'install', 'cmake', 'ninja', 'mpfr', 'gmp', ] + BoostInfo().dependencies('darwin-homebrew') self.log.info('Executing: ' + ' '.join(packages)) self.execute(*packages) elif exists('/sw/bin/fink'): self.log.info('Looks like you are using Fink on macOS') self.log.error("I don't know the package names for Fink yet!") sys.exit(1) elif system == 'Linux': if exists('/etc/issue'): issue = open('/etc/issue') issue_name = issue.readline() if issue_name.startswith('Ubuntu'): info = dict([line.strip().split('=', 1) for line in open('/etc/lsb-release')]) release = info['DISTRIB_CODENAME'] self.log.info('Looks like you are using APT on Ubuntu ' + release) packages = [ 'sudo', 'apt-get', 'install', 'build-essential', ] if release == 'focal': packages.extend([ 'doxygen', 'cmake', 'ninja-build', 'zlib1g-dev', 'libbz2-dev', 'python-dev', 'libgmp3-dev', 'libmpfr-dev', 'gettext', 'libedit-dev', 'texinfo', 'lcov', 'libutfcpp-dev', 'sloccount' ]) elif release == 'bionic': packages.extend([ 'doxygen', 'cmake', 'ninja-build', 'zlib1g-dev', 'libbz2-dev', 'python-dev', 'libgmp3-dev', 'libmpfr-dev', 'gettext', 'libedit-dev', 'texinfo', 'lcov', 'libutfcpp-dev', 'sloccount' ]) elif release == 'trusty': packages.extend([ 'doxygen', 'cmake', 'ninja-build', 'zlib1g-dev', 'libbz2-dev', 'python-dev', 'libgmp3-dev', 'libmpfr-dev', 'gettext', 'libedit-dev', 'texinfo', 'lcov', 'libutfcpp-dev', 'sloccount' ]) elif release == 'eoan': packages.extend([ 'doxygen', 'cmake', 'ninja-build', 'zlib1g-dev', 'libbz2-dev', 'python-dev', 'libgmp3-dev', 'libmpfr-dev', 'gettext', 'libedit-dev', 'texinfo', 'lcov', 'libutfcpp-dev', 'sloccount' ]) elif release == 'xenial': packages.extend([ 'doxygen', 'cmake', 'ninja-build', 'zlib1g-dev', 'libbz2-dev', 'python-dev', 'libgmp3-dev', 'libmpfr-dev', 'gettext', 'libedit-dev', 'texinfo', 'lcov', 'libutfcpp-dev', 'sloccount' ]) elif release == 'saucy': packages.extend([ 'doxygen', 'cmake', 'ninja-build', 'zlib1g-dev', 'libbz2-dev', 'python-dev', 'libgmp-dev', 'libmpfr-dev', 'gettext', 'libedit-dev', 'texinfo', 'lcov', 'sloccount' ]) elif release == 'precise': packages.extend([ 'libtool', 'cmake', 'zlib1g-dev', 'libbz2-dev', 'python-dev', 'libgmp-dev', 'libmpfr-dev', 'gettext', 'libedit-dev', 'texinfo', 'lcov', 'libutfcpp-dev', 'sloccount' ]) else: self.log.info('I do not recognize your version of Ubuntu!') packages = None if packages: packages.extend( BoostInfo().dependencies('ubuntu-' + release)) self.log.info('Executing: ' + ' '.join(packages)) self.execute(*packages) elif issue_name.startswith('Arch Linux'): self.log.info('Looks like you are using Pacman on Arch Linux') packages = [ 'sudo', 'pacman', '-S', '--noconfirm', 'base-devel', 'boost', 'doxygen', 'gmp', 'graphviz', 'lcov', 'libedit', 'mpfr', 'sloccount', 'utf8cpp' ] self.log.info('Executing: ' + ' '.join(packages)) self.execute(*packages) if exists('/etc/redhat-release'): release = open('/etc/redhat-release').readline() if release.startswith('CentOS'): self.log.info('Looks like you are using YUM on CentOS') packages = [ 'sudo', 'yum', 'install', 'gcc', 'gcc-c++', 'compat-gcc-*', 'make', 'libtool', 'autoconf', 'automake', 'zlib-devel', 'bzip2-devel', 'python-devel', 'gmp-devel', 'gettext-devel', #'mpfr-devel' 'libedit-devel', #'texlive-full', #'doxygen', #'graphviz', 'texinfo', #'lcov', #'sloccount' ] self.log.info('Executing: ' + ' '.join(packages)) self.execute(*packages) elif release.startswith('Fedora release 20'): self.log.info('Looks like you are using YUM on Fedora 20') packages = [ 'sudo', 'yum', 'install', 'boost-devel', 'bzip2-devel', 'cmake', 'doxygen', 'gcc', 'gcc-c++', 'gettext', 'gettext-devel', 'gmp-devel', 'lcov', 'libedit-devel', 'mpfr-devel', 'ninja-build', 'python-devel', 'sloccount', 'texinfo', 'zlib-devel' ] self.log.info('Executing: ' + ' '.join(packages)) self.execute(*packages) elif system.startswith('CYGWIN'): self.log.info('Looks like you are using Cygwin') self.log.info('Please install the dependencies manually.') ######################################################################### # Determine the system's basic configuration # ######################################################################### def setup_for_johnw(self): self.configure_args.append('-DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON') if not self.options.compiler: self.configure_args.append('-DCMAKE_CXX_COMPILER:PATH=/usr/local/bin/clang++') if self.current_flavor == 'opt': self.configure_args.append('-DCMAKE_CXX_FLAGS_RELEASE:STRING=-O3') self.configure_args.append('-DCMAKE_EXE_LINKER_FLAGS:STRING=-O3') self.configure_args.append('-DCMAKE_SHARED_LINKER_FLAGS:STRING=-O3') self.configure_args.append('-DCMAKE_MODULE_LINKER_FLAGS:STRING=-O3') #else: # self.CXXFLAGS.append('-g -O1 -faddress-sanitizer') # self.LDFLAGS.append('-g -O1 -faddress-sanitizer') self.configure_args.append(self.source_dir) else: self.configure_args.append('-DCMAKE_CXX_COMPILER:PATH=' + self.options.compiler) self.configure_args.append('-DCMAKE_INCLUDE_PATH:STRING=/usr/local/include') self.configure_args.append('-DCMAKE_LIBRARY_PATH:STRING=/usr/local/lib') self.configure_args.append('-DBOOST_ROOT=/usr/local') self.configure_args.append(self.source_dir) def setup_for_system(self): system = self.get_stdout('uname', '-s').decode(locale.getpreferredencoding()) self.log.info('System type is => ' + system) if self.options.enable_doxygen: self.configure_args.append('-DUSE_DOXYGEN=1') if self.options.python: self.configure_args.append('-DUSE_PYTHON=1') if system.startswith('CYGWIN'): self.configure_args.append('-G') self.configure_args.append('Unix Makefiles') elif system.startswith('MINGW'): self.configure_args.append('-G') self.configure_args.append('MSYS Makefiles') elif self.options.use_ninja: self.configure_args.append('-GNinja') if exists('/Users/johnw/Projects/ledger/plan/TODO'): self.setup_for_johnw() def setup_flavor(self): self.setup_for_system() if 'setup_flavor_' + self.current_flavor not in PrepareBuild.__dict__: self.log.error('Unknown build flavor "%s"' % self.current_flavor) sys.exit(1) self.log.info('Setting up build flavor => ' + self.current_flavor) PrepareBuild.__dict__['setup_flavor_' + self.current_flavor](self) def escape_string(self, data): return re.sub('(["\\\\])', '\\\\\\1', data) def finalize_config(self): self.setup_flavor() for var in ('CXXFLAGS', 'LDFLAGS'): value = self.__dict__[var] if value: first = not self.envvars[var] for member in value: #escaped = self.escape_string(member) #if member != escaped: # member = escaped if first: first = False else: self.envvars[var] += ' ' self.envvars[var] += member self.log.debug('Final value of %s: %s' % (var, self.envvars[var])) elif var in self.envvars: del self.envvars[var] ######################################################################### # Options that can modify any build flavor # ######################################################################### def option_local(self, option=None, opt_str=None, value=None, parser=None): self.log.debug('Saw option --local') self.options.build_dir = self.source_dir def option_help(self, option=None, opt_str=None, value=None, parser=None): self.phase_help() ######################################################################### # The various build flavors # ######################################################################### def setup_flavor_default(self): pass def setup_flavor_debug(self): self.configure_args.append('-DBUILD_DEBUG=1') def setup_flavor_opt(self): self.configure_args.append('-DBUILD_DEBUG=0') self.configure_args.append('-DNO_ASSERTS=1') def setup_flavor_gcov(self): # NO_ASSERTS is set so that branch coverage ignores the never-taken # else branch inside assert statements. self.configure_args.append('-DBUILD_DEBUG=1') self.configure_args.append('-DNO_ASSERTS=1') self.configure_args.append('-DCLANG_GCOV=1') self.CXXFLAGS.append('-fprofile-arcs') self.CXXFLAGS.append('-ftest-coverage') self.LDFLAGS.append('-fprofile-arcs') self.LDFLAGS.append('-ftest-coverage') if not self.options.compiler or self.options.compiler == "clang-3.1": self.LDFLAGS.append('-lgcov') def setup_flavor_gprof(self): self.configure_args.append('-DBUILD_DEBUG=1') self.CXXFLAGS.append('-pg') self.LDFLAGS.append('-pg') ######################################################################### # Configure build tree using CMake # ######################################################################### def configure_environment(self): self.finalize_config() environ = dict(os.environ) for key, value in self.envvars.items(): if value: environ[key] = value if self.build_directory() == self.source_dir: conf_args = ['cmake'] else: conf_args = ['cmake', self.source_dir] if not which('cmake'): self.log.error("Cannot find CMake, please check your PATH") sys.exit(1) for var in ('CXX', 'CXXFLAGS', 'LDFLAGS'): if self.envvars.get(var) and (var.endswith('FLAGS') or exists(self.envvars[var])): if var == 'CXX': conf_args.append('-DCMAKE_CXX_COMPILER=%s' % self.envvars[var]) elif var == 'CXXFLAGS': conf_args.append('-DCMAKE_CXX_FLAGS=%s' % self.envvars[var]) elif var == 'LDFLAGS': conf_args.append('-DCMAKE_EXE_LINKER_FLAGS=%s' % self.envvars[var]) if self.options.boost_root: conf_args.append('-DBOOST_ROOT=%s' % self.options.boost_root) conf_args.append('-DBoost_NO_SYSTEM_PATHS=TRUE') if self.options.boost_suffix: conf_args.append('-DBoost_COMPILER=%s' % self.options.boost_suffix) if self.options.boost_include: conf_args.append('-DBOOST_INCLUDEDIR=%s' % self.options.boost_include) if self.prefix_directory(): conf_args.append('-DCMAKE_INSTALL_PREFIX=%s' % self.prefix_directory()) return (environ, conf_args + self.configure_args) def phase_configure(self, *args): self.log.info('Executing phase: configure') self.configured = True environ, conf_args = self.configure_environment() for arg in args: if arg: conf_args.append(arg) build_dir = self.ensure(self.build_directory()) try: os.chdir(build_dir) need_to_config = not isfile('rules.ninja' if self.options.use_ninja else 'Makefile') if need_to_config: self.log.debug('Source => ' + self.source_dir) self.log.debug('Build => ' + build_dir) self.log.debug('configure env => ' + str(environ)) self.log.debug('configure args => ' + str(conf_args)) configure = Popen(conf_args, shell=False, env=environ) retcode = configure.wait() if retcode < 0: self.log.error("Child was terminated by signal", -retcode) sys.exit(1) elif retcode != 0: self.log.error("Execution failed: " + ' '.join(conf_args)) sys.exit(1) else: self.log.debug('configure does not need to be run') finally: os.chdir(self.source_dir) def phase_config(self, *args): self.log.info('Executing phase: config') self.phase_configure(*args) if self.should_clean: self.phase_clean() ######################################################################### # Builds products from the sources # ######################################################################### def phase_make(self, *args): self.log.info('Executing phase: make') config_args = [] make_args = [] for arg in args: if arg.startswith('--') or arg.startswith('-D'): config_args.append(arg) else: make_args.append(arg) if self.options.jobs > 1 and self.current_flavor != 'gcov': make_args.append('-j%d' % self.options.jobs) if self.options.verbose: make_args.append('-v' if self.options.use_ninja else 'VERBOSE=1') self.log.debug('Configure arguments => ' + str(config_args)) self.log.debug('Makefile arguments => ' + str(make_args)) if not self.configured: self.phase_config(*config_args) build_dir = self.ensure(self.build_directory()) try: self.log.debug('Changing directory to ' + build_dir) os.chdir(build_dir) self.execute(*(['ninja' if self.options.use_ninja else 'make'] + make_args)) finally: os.chdir(self.source_dir) def phase_check(self, *args): self.log.info('Executing phase: check') build_dir = self.ensure(self.build_directory()) try: self.log.debug('Changing directory to ' + build_dir) os.chdir(build_dir) make_args = list(args) if self.options.jobs > 1: make_args.append('-j%d' % self.options.jobs) self.execute(*(['ctest'] + list(make_args))) finally: os.chdir(self.source_dir) def phase_update(self, *args): self.log.info('Executing phase: update') self.phase_pull() self.phase_make(*args) ######################################################################### # Build directory cleaning phases # ######################################################################### def phase_clean(self, *args): self.log.info('Executing phase: clean') self.phase_make('clean') def phase_gitclean(self, *args): self.log.info('Executing phase: gitclean') if self.git_working_tree(): self.execute('git', 'clean', '-dfx') ######################################################################### # Other build phases # ######################################################################### def configure_flavor(self, flavor, reset=True): self.initialize() # reset everything self.current_flavor = flavor self.options.build_dir = None # use the build/ tree self.options.prefix_dir = None if reset and exists(self.build_directory()) and \ isdir(self.build_directory()): self.log.info('=== Wiping build directory %s ===' % self.build_directory()) try: shutil.rmtree(self.build_directory()) except: self.execute('chmod', '-R', 'u+w', self.build_directory()) self.execute('rm', '-fr', self.build_directory()) def phase_rsync(self, *args): self.log.info('Executing phase: rsync') proof_dir = 'ledger-proof' if self.options.python: proof_dir += "-python" if self.options.compiler: proof_dir += "-" + basename(self.options.compiler) source_copy_dir = join(self.ensure(self.products_directory()), proof_dir) self.execute('rsync', '-a', '--delete', '--exclude=/dist/', '--exclude=.git/', '--exclude=b/', '--exclude=/lib/boost-release/', '--exclude=/archive/', '--exclude=/build/', '%s/' % self.source_dir, '%s/' % source_copy_dir) self.source_dir = source_copy_dir def phase_proof(self, *args): self.log.info('Executing phase: proof') self.log.info('=== Copying source tree ===') self.phase_rsync() self.phase_makeall(reset=True, *args) self.configure_flavor('opt', reset=False) self.log.info('=== Testing opt ===') # jww (2012-05-20): Can't use fullcheck yet #self.phase_make('fullcheck') self.phase_make('test') self.configure_flavor('gcov', reset=False) self.log.info('=== Testing gcov ===') #self.phase_make('check') self.phase_make('test') self.configure_flavor('default', reset=False) self.log.info('=== Testing default ===') #self.phase_make('fullcheck') self.phase_make('test') # jww (2012-05-20): docs are not working yet #self.phase_make('docs') self.configure_flavor('debug', reset=False) self.log.info('=== Testing debug ===') #self.phase_make('fullcheck') self.phase_make('test') def phase_makeall(self, reset=False, *args): self.log.info('Executing phase: makeall') self.configure_flavor('opt', reset) self.log.info('=== Building opt ===') self.phase_make(*args) self.configure_flavor('gcov', reset) self.log.info('=== Building gcov ===') self.phase_make(*args) self.configure_flavor('default', reset) self.log.info('=== Building default ===') self.phase_make(*args) self.configure_flavor('debug', reset) self.log.info('=== Building debug ===') self.phase_make(*args) ######################################################################### # Help # ######################################################################### def phase_help(self, *args): self.option_parser.print_help() print(""" Of the optional ARGS, the first is an optional build FLAVOR, with the default being 'debug': default Regular autoconf settings debug Debugging and --verify support (default) opt Full optimizations gcov Coverage analysis gprof Code profiling (for macOS, just use: 'shark -i ledger ...') Next is the optional build PHASE, with 'config' being the default: clean Runs 'make clean' in the build directory config Configure the environment for building dependencies Automatically install all necessary build dependencies gitclean Runs 'git clean -dfx', which *really* cleans things help Displays this help text info Show information about the build environment make Do a make in the build directory proof Proves Ledger by building and testing every flavor pull Pulls the latest, and updates local config if need be update Does it all, updates your environment and re-make's There are many other build phases, though most are not of interest to the typical user: configure Runs just cmake do_all Runs makeall followed by proof gettext Initialize gettext support makeall Build every flavor there is products Report the products directory path rsync Rsync a copy of the source tree into Products sloc Report total Source Lines Of Code version Output current HEAD version to version.m4 NOTE: If you wish to pass options to CMake or make, add "--" followed by your options. Those starting with "-D" or "--" will be passed on to CMake, positional arguments and other options will be passed to make. For the 'config' and 'configure' phase everything will be passed to CMake. Here are some real-world examples: ./acprep ./acprep --python ./acprep opt make ./acprep make doc -- -DBUILD_WEB_DOCS=1""") sys.exit(0) PrepareBuild().run() ledger-3.3.2/appveyor.yml000066400000000000000000000031311441123640000153420ustar00rootroot00000000000000version: build-{build} image: Visual Studio 2013 shallow_clone: true # don't download repo history install: - time /t - tzutil /s "Eastern Standard Time_dstoff" - time /t branches: only: - master - next build_script: - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sy pacman" - C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -Sy pacman-mirrors" - C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -Syu" - C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -S mingw-w64-i686-boost" - C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -S mingw-w64-i686-mpfr" - C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -S mingw-w64-i686-cmake" - C:\msys64\usr\bin\bash -lc "export PATH=/mingw32/bin:$PATH && cd $APPVEYOR_BUILD_FOLDER && cmake -G 'MSYS Makefiles'" - C:\msys64\usr\bin\bash -lc "export PATH=/mingw32/bin:$PATH && cd $APPVEYOR_BUILD_FOLDER && make -j2" after_build: - set LIB_DIR=C:\msys64\mingw32\bin - 7z a ledger-win.zip %APPVEYOR_BUILD_FOLDER%\ledger.exe %LIB_DIR%\libboost_filesystem-mt.dll %LIB_DIR%\libboost_regex-mt.dll %LIB_DIR%\libboost_system-mt.dll %LIB_DIR%\libgcc_s_dw2-1.dll %LIB_DIR%\libgmp-10.dll %LIB_DIR%\libicudt57.dll %LIB_DIR%\libicuuc57.dll %LIB_DIR%\libstdc++-6.dll %LIB_DIR%\libwinpthread-1.dll %APPVEYOR_BUILD_FOLDER%\libledger.dll artifacts: - path: ledger-win.zip name: Ledger Win32 binaries test_script: - C:\msys64\usr\bin\bash -lc "export MINGW_PREFIX=C:/msys64/mingw32/ CTEST_OUTPUT_ON_FAILURE=1 PATH=/mingw32/bin:$PATH && cd $APPVEYOR_BUILD_FOLDER && make test || echo Errors from tests were ignored" ledger-3.3.2/cmake/000077500000000000000000000000001441123640000140345ustar00rootroot00000000000000ledger-3.3.2/cmake/FindUtfcpp.cmake000066400000000000000000000013521441123640000171010ustar00rootroot00000000000000# - Try to find utfcpp # Once done, this will define # # UTFCPP_FOUND - system has utfcpp's utf8.h # UTFCPP_PATH - the utfcpp include directories include(CheckCXXSourceCompiles) set(UTFCPP_FOUND FALSE) find_path(UTFCPP_INCLUDE_DIR NAMES utf8.h HINTS "${UTFCPP_PATH}" PATHS "${PROJECT_SOURCE_DIR}/lib/utfcpp/v3/source" ) if (UTFCPP_INCLUDE_DIR) set(CMAKE_REQUIRED_INCLUDES "${UTFCPP_INCLUDE_DIR}") set(UTFCPP_FOUND TRUE) endif() check_cxx_source_compiles(" #include #include \"utf8.h\" int main(int argc, char** argv) { std::string input = std::string(\"utfcpp\"); const char * p = input.c_str(); std::size_t len = input.length(); utf8::is_valid(p, p + len); }" HAVE_WORKING_UTFCPP) ledger-3.3.2/cmake/python-backport/000077500000000000000000000000001441123640000171605ustar00rootroot00000000000000ledger-3.3.2/cmake/python-backport/FindPackageHandleStandardArgs.cmake000066400000000000000000000351011441123640000257300ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindPackageHandleStandardArgs ----------------------------- This module provides a function intended to be used in :ref:`Find Modules` implementing :command:`find_package()` calls. It handles the ``REQUIRED``, ``QUIET`` and version-related arguments of ``find_package``. It also sets the ``_FOUND`` variable. The package is considered found if all variables listed contain valid results, e.g. valid filepaths. .. command:: find_package_handle_standard_args There are two signatures:: find_package_handle_standard_args( (DEFAULT_MSG|) ... ) find_package_handle_standard_args( [FOUND_VAR ] [REQUIRED_VARS ...] [VERSION_VAR ] [HANDLE_COMPONENTS] [CONFIG_MODE] [FAIL_MESSAGE ] ) The ``_FOUND`` variable will be set to ``TRUE`` if all the variables ``...`` are valid and any optional constraints are satisfied, and ``FALSE`` otherwise. A success or failure message may be displayed based on the results and on whether the ``REQUIRED`` and/or ``QUIET`` option was given to the :command:`find_package` call. The options are: ``(DEFAULT_MSG|)`` In the simple signature this specifies the failure message. Use ``DEFAULT_MSG`` to ask for a default message to be computed (recommended). Not valid in the full signature. ``FOUND_VAR `` Obsolete. Specifies either ``_FOUND`` or ``_FOUND`` as the result variable. This exists only for compatibility with older versions of CMake and is now ignored. Result variables of both names are always set for compatibility. ``REQUIRED_VARS ...`` Specify the variables which are required for this package. These may be named in the generated failure message asking the user to set the missing variable values. Therefore these should typically be cache entries such as ``FOO_LIBRARY`` and not output variables like ``FOO_LIBRARIES``. ``VERSION_VAR `` Specify the name of a variable that holds the version of the package that has been found. This version will be checked against the (potentially) specified required version given to the :command:`find_package` call, including its ``EXACT`` option. The default messages include information about the required version and the version which has been actually found, both if the version is ok or not. ``HANDLE_COMPONENTS`` Enable handling of package components. In this case, the command will report which components have been found and which are missing, and the ``_FOUND`` variable will be set to ``FALSE`` if any of the required components (i.e. not the ones listed after the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are missing. ``CONFIG_MODE`` Specify that the calling find module is a wrapper around a call to ``find_package( NO_MODULE)``. This implies a ``VERSION_VAR`` value of ``_VERSION``. The command will automatically check whether the package configuration file was found. ``FAIL_MESSAGE `` Specify a custom failure message instead of using the default generated message. Not recommended. Example for the simple signature: .. code-block:: cmake find_package_handle_standard_args(LibXml2 DEFAULT_MSG LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) The ``LibXml2`` package is considered to be found if both ``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found and ``REQUIRED`` was used, it fails with a :command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was used or not. If it is found, success will be reported, including the content of the first ````. On repeated CMake runs, the same message will not be printed again. Example for the full signature: .. code-block:: cmake find_package_handle_standard_args(LibArchive REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR VERSION_VAR LibArchive_VERSION) In this case, the ``LibArchive`` package is considered to be found if both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. Also the version of ``LibArchive`` will be checked by using the version contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, the default messages will be printed. Another example for the full signature: .. code-block:: cmake find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) find_package_handle_standard_args(Automoc4 CONFIG_MODE) In this case, a ``FindAutmoc4.cmake`` module wraps a call to ``find_package(Automoc4 NO_MODULE)`` and adds an additional search directory for ``automoc4``. Then the call to ``find_package_handle_standard_args`` produces a proper success/failure message. #]=======================================================================] include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) # internal helper macro macro(_FPHSA_FAILURE_MESSAGE _msg) if (${_NAME}_FIND_REQUIRED) message(FATAL_ERROR "${_msg}") else () if (NOT ${_NAME}_FIND_QUIETLY) message(STATUS "${_msg}") endif () endif () endmacro() # internal helper macro to generate the failure message when used in CONFIG_MODE: macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: if(${_NAME}_CONFIG) _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") else() # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. # List them all in the error message: if(${_NAME}_CONSIDERED_CONFIGS) set(configsText "") list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) math(EXPR configsCount "${configsCount} - 1") foreach(currentConfigIndex RANGE ${configsCount}) list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) string(APPEND configsText " ${filename} (version ${version})\n") endforeach() if (${_NAME}_NOT_FOUND_MESSAGE) string(APPEND configsText " Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n") endif() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}") else() # Simple case: No Config-file was found at all: _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") endif() endif() endmacro() function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) # Set up the arguments for `cmake_parse_arguments`. set(options CONFIG_MODE HANDLE_COMPONENTS) set(oneValueArgs FAIL_MESSAGE VERSION_VAR FOUND_VAR) set(multiValueArgs REQUIRED_VARS) # Check whether we are in 'simple' or 'extended' mode: set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) if(${INDEX} EQUAL -1) set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) set(FPHSA_REQUIRED_VARS ${ARGN}) set(FPHSA_VERSION_VAR) else() cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) if(FPHSA_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") endif() if(NOT FPHSA_FAIL_MESSAGE) set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") endif() # In config-mode, we rely on the variable _CONFIG, which is set by find_package() # when it successfully found the config-file, including version checking: if(FPHSA_CONFIG_MODE) list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) set(FPHSA_VERSION_VAR ${_NAME}_VERSION) endif() if(NOT FPHSA_REQUIRED_VARS) message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") endif() endif() # now that we collected all arguments, process them if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") endif() list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) string(TOUPPER ${_NAME} _NAME_UPPER) string(TOLOWER ${_NAME} _NAME_LOWER) if(FPHSA_FOUND_VAR) if(FPHSA_FOUND_VAR MATCHES "^${_NAME}_FOUND$" OR FPHSA_FOUND_VAR MATCHES "^${_NAME_UPPER}_FOUND$") set(_FOUND_VAR ${FPHSA_FOUND_VAR}) else() message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_NAME}_FOUND\" and \"${_NAME_UPPER}_FOUND\" are valid names.") endif() else() set(_FOUND_VAR ${_NAME_UPPER}_FOUND) endif() # collect all variables which were not found, so they can be printed, so the # user knows better what went wrong (#6375) set(MISSING_VARS "") set(DETAILS "") # check if all passed variables are valid set(FPHSA_FOUND_${_NAME} TRUE) foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) if(NOT ${_CURRENT_VAR}) set(FPHSA_FOUND_${_NAME} FALSE) string(APPEND MISSING_VARS " ${_CURRENT_VAR}") else() string(APPEND DETAILS "[${${_CURRENT_VAR}}]") endif() endforeach() if(FPHSA_FOUND_${_NAME}) set(${_NAME}_FOUND TRUE) set(${_NAME_UPPER}_FOUND TRUE) else() set(${_NAME}_FOUND FALSE) set(${_NAME_UPPER}_FOUND FALSE) endif() # component handling unset(FOUND_COMPONENTS_MSG) unset(MISSING_COMPONENTS_MSG) if(FPHSA_HANDLE_COMPONENTS) foreach(comp ${${_NAME}_FIND_COMPONENTS}) if(${_NAME}_${comp}_FOUND) if(NOT DEFINED FOUND_COMPONENTS_MSG) set(FOUND_COMPONENTS_MSG "found components: ") endif() string(APPEND FOUND_COMPONENTS_MSG " ${comp}") else() if(NOT DEFINED MISSING_COMPONENTS_MSG) set(MISSING_COMPONENTS_MSG "missing components: ") endif() string(APPEND MISSING_COMPONENTS_MSG " ${comp}") if(${_NAME}_FIND_REQUIRED_${comp}) set(${_NAME}_FOUND FALSE) string(APPEND MISSING_VARS " ${comp}") endif() endif() endforeach() set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") string(APPEND DETAILS "[c${COMPONENT_MSG}]") endif() # version handling: set(VERSION_MSG "") set(VERSION_OK TRUE) # check with DEFINED here as the requested or found version may be "0" if (DEFINED ${_NAME}_FIND_VERSION) if(DEFINED ${FPHSA_VERSION_VAR}) set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) if(${_NAME}_FIND_VERSION_EXACT) # exact version required # count the dots in the version string string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${_FOUND_VERSION}") # add one dot because there is one dot more than there are components string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS) if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT) # Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT # is at most 4 here. Therefore a simple lookup table is used. if (${_NAME}_FIND_VERSION_COUNT EQUAL 1) set(_VERSION_REGEX "[^.]*") elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2) set(_VERSION_REGEX "[^.]*\\.[^.]*") elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3) set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*") else () set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") endif () string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${_FOUND_VERSION}") unset(_VERSION_REGEX) if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD) set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") set(VERSION_OK FALSE) else () set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") endif () unset(_VERSION_HEAD) else () if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _FOUND_VERSION) set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") set(VERSION_OK FALSE) else () set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") endif () endif () unset(_VERSION_DOTS) else() # minimum version specified: if (${_NAME}_FIND_VERSION VERSION_GREATER _FOUND_VERSION) set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") set(VERSION_OK FALSE) else () set(VERSION_MSG "(found suitable version \"${_FOUND_VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")") endif () endif() else() # if the package was not found, but a version was given, add that to the output: if(${_NAME}_FIND_VERSION_EXACT) set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") else() set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") endif() endif() else () # Check with DEFINED as the found version may be 0. if(DEFINED ${FPHSA_VERSION_VAR}) set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") endif() endif () if(VERSION_OK) string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") else() set(${_NAME}_FOUND FALSE) endif() # print the result: if (${_NAME}_FOUND) FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") else () if(FPHSA_CONFIG_MODE) _FPHSA_HANDLE_FAILURE_CONFIG_MODE() else() if(NOT VERSION_OK) _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") else() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") endif() endif() endif () set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) endfunction() ledger-3.3.2/cmake/python-backport/FindPackageMessage.cmake000066400000000000000000000032571441123640000236320ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindPackageMessage ------------------ .. code-block:: cmake find_package_message( "message for user" "find result details") This function is intended to be used in FindXXX.cmake modules files. It will print a message once for each unique find result. This is useful for telling the user where a package was found. The first argument specifies the name (XXX) of the package. The second argument specifies the message to display. The third argument lists details about the find result so that if they change the message will be displayed again. The macro also obeys the QUIET argument to the find_package command. Example: .. code-block:: cmake if(X11_FOUND) find_package_message(X11 "Found X11: ${X11_X11_LIB}" "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") else() ... endif() #]=======================================================================] function(find_package_message pkg msg details) # Avoid printing a message repeatedly for the same find result. if(NOT ${pkg}_FIND_QUIETLY) string(REPLACE "\n" "" details "${details}") set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") # The message has not yet been printed. message(STATUS "${msg}") # Save the find details in the cache to avoid printing the same # message again. set("${DETAILS_VAR}" "${details}" CACHE INTERNAL "Details about finding ${pkg}") endif() endif() endfunction() ledger-3.3.2/cmake/python-backport/FindPython.cmake000066400000000000000000000217571441123640000222600ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindPython ---------- Find Python interpreter, compiler and development environment (include directories and libraries). Three components are supported: * ``Interpreter``: search for Python interpreter. * ``Compiler``: search for Python compiler. Only offered by IronPython. * ``Development``: search for development artifacts (include directories and libraries). * ``NumPy``: search for NumPy include directories. If no ``COMPONENTS`` is specified, ``Interpreter`` is assumed. To ensure consistent versions between components ``Interpreter``, ``Compiler``, ``Development`` and ``NumPy``, specify all components at the same time:: find_package (Python COMPONENTS Interpreter Development) This module looks preferably for version 3 of Python. If not found, version 2 is searched. To manage concurrent versions 3 and 2 of Python, use :module:`FindPython3` and :module:`FindPython2` modules rather than this one. .. note:: If components ``Interpreter`` and ``Development`` are both specified, this module search only for interpreter with same platform architecture as the one defined by ``CMake`` configuration. This constraint does not apply if only ``Interpreter`` component is specified. Imported Targets ^^^^^^^^^^^^^^^^ This module defines the following :ref:`Imported Targets ` (when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``): ``Python::Interpreter`` Python interpreter. Target defined if component ``Interpreter`` is found. ``Python::Compiler`` Python compiler. Target defined if component ``Compiler`` is found. ``Python::Python`` Python library for Python embedding. Target defined if component ``Development`` is found. ``Python::Module`` Python library for Python module. Target defined if component ``Development`` is found. ``Python::NumPy`` NumPy Python library. Target defined if component ``NumPy`` is found. Result Variables ^^^^^^^^^^^^^^^^ This module will set the following variables in your project (see :ref:`Standard Variable Names `): ``Python_FOUND`` System has the Python requested components. ``Python_Interpreter_FOUND`` System has the Python interpreter. ``Python_EXECUTABLE`` Path to the Python interpreter. ``Python_INTERPRETER_ID`` A short string unique to the interpreter. Possible values include: * Python * ActivePython * Anaconda * Canopy * IronPython ``Python_STDLIB`` Standard platform independent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=True)``. ``Python_STDARCH`` Standard platform dependent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=True)``. ``Python_SITELIB`` Third-party platform independent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=False)``. ``Python_SITEARCH`` Third-party platform dependent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=False)``. ``Python_Compiler_FOUND`` System has the Python compiler. ``Python_COMPILER`` Path to the Python compiler. Only offered by IronPython. ``Python_COMPILER_ID`` A short string unique to the compiler. Possible values include: * IronPython ``Python_Development_FOUND`` System has the Python development artifacts. ``Python_INCLUDE_DIRS`` The Python include directories. ``Python_LIBRARIES`` The Python libraries. ``Python_LIBRARY_DIRS`` The Python library directories. ``Python_RUNTIME_LIBRARY_DIRS`` The Python runtime library directories. ``Python_VERSION`` Python version. ``Python_VERSION_MAJOR`` Python major version. ``Python_VERSION_MINOR`` Python minor version. ``Python_VERSION_PATCH`` Python patch version. ``Python_NumPy_FOUND`` System has the NumPy. ``Python_NumPy_INCLUDE_DIRS`` The NumPy include directories. ``Python_NumPy_VERSION`` The NumPy version. Hints ^^^^^ ``Python_ROOT_DIR`` Define the root directory of a Python installation. ``Python_USE_STATIC_LIBS`` * If not defined, search for shared libraries and static libraries in that order. * If set to TRUE, search **only** for static libraries. * If set to FALSE, search **only** for shared libraries. ``Python_FIND_STRATEGY`` This variable defines how lookup will be done. The ``Python_FIND_STRATEGY`` variable can be set to empty or one of the following: * ``VERSION``: Try to find the most recent version in all specified locations. This is the default if policy :policy:`CMP0094` is undefined or set to ``OLD``. * ``LOCATION``: Stops lookup as soon as a version satisfying version constraints is founded. This is the default if policy :policy:`CMP0094` is set to ``NEW``. ``Python_FIND_REGISTRY`` On Windows the ``Python_FIND_REGISTRY`` variable determine the order of preference between registry and environment variables. the ``Python_FIND_REGISTRY`` variable can be set to empty or one of the following: * ``FIRST``: Try to use registry before environment variables. This is the default. * ``LAST``: Try to use registry after environment variables. * ``NEVER``: Never try to use registry. ``Python_FIND_FRAMEWORK`` On macOS the ``Python_FIND_FRAMEWORK`` variable determine the order of preference between Apple-style and unix-style package components. This variable can be set to empty or take same values as :variable:`CMAKE_FIND_FRAMEWORK` variable. .. note:: Value ``ONLY`` is not supported so ``FIRST`` will be used instead. If ``Python_FIND_FRAMEWORK`` is not defined, :variable:`CMAKE_FIND_FRAMEWORK` variable will be used, if any. ``Python_FIND_VIRTUALENV`` This variable defines the handling of virtual environments. It is meaningful only when a virtual environment is active (i.e. the ``activate`` script has been evaluated). In this case, it takes precedence over ``Python_FIND_REGISTRY`` and ``CMAKE_FIND_FRAMEWORK`` variables. The ``Python_FIND_VIRTUALENV`` variable can be set to empty or one of the following: * ``FIRST``: The virtual environment is used before any other standard paths to look-up for the interpreter. This is the default. * ``ONLY``: Only the virtual environment is used to look-up for the interpreter. * ``STANDARD``: The virtual environment is not used to look-up for the interpreter. In this case, variable ``Python_FIND_REGISTRY`` (Windows) or ``CMAKE_FIND_FRAMEWORK`` (macOS) can be set with value ``LAST`` or ``NEVER`` to select preferably the interpreter from the virtual environment. Commands ^^^^^^^^ This module defines the command ``Python_add_library`` (when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as :command:`add_library` and adds a dependency to target ``Python::Python`` or, when library type is ``MODULE``, to target ``Python::Module`` and takes care of Python module naming rules:: Python_add_library (my_module MODULE src1.cpp) If library type is not specified, ``MODULE`` is assumed. #]=======================================================================] set (_PYTHON_PREFIX Python) if (DEFINED Python_FIND_VERSION) set (_Python_REQUIRED_VERSION_MAJOR ${Python_FIND_VERSION_MAJOR}) include (${CMAKE_CURRENT_LIST_DIR}/FindPython/Support.cmake) else() # iterate over versions in quiet and NOT required modes to avoid multiple # "Found" messages and prematurally failure. set (_Python_QUIETLY ${Python_FIND_QUIETLY}) set (_Python_REQUIRED ${Python_FIND_REQUIRED}) set (Python_FIND_QUIETLY TRUE) set (Python_FIND_REQUIRED FALSE) set (_Python_REQUIRED_VERSIONS 3 2) set (_Python_REQUIRED_VERSION_LAST 2) foreach (_Python_REQUIRED_VERSION_MAJOR IN LISTS _Python_REQUIRED_VERSIONS) set (Python_FIND_VERSION ${_Python_REQUIRED_VERSION_MAJOR}) include (${CMAKE_CURRENT_LIST_DIR}/FindPython/Support.cmake) if (Python_FOUND OR _Python_REQUIRED_VERSION_MAJOR EQUAL _Python_REQUIRED_VERSION_LAST) break() endif() # clean-up some CACHE variables to ensure look-up restart from scratch foreach (_Python_ITEM IN LISTS _Python_CACHED_VARS) unset (${_Python_ITEM} CACHE) endforeach() endforeach() unset (Python_FIND_VERSION) set (Python_FIND_QUIETLY ${_Python_QUIETLY}) set (Python_FIND_REQUIRED ${_Python_REQUIRED}) if (Python_FIND_REQUIRED OR NOT Python_FIND_QUIETLY) # call again validation command to get "Found" or error message find_package_handle_standard_args (Python HANDLE_COMPONENTS REQUIRED_VARS ${_Python_REQUIRED_VARS} VERSION_VAR Python_VERSION) endif() endif() if (COMMAND __Python_add_library) macro (Python_add_library) __Python_add_library (Python ${ARGV}) endmacro() endif() unset (_PYTHON_PREFIX) ledger-3.3.2/cmake/python-backport/FindPython/000077500000000000000000000000001441123640000212425ustar00rootroot00000000000000ledger-3.3.2/cmake/python-backport/FindPython/Support.cmake000066400000000000000000002503301441123640000237230ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. # # This file is a "template" file used by various FindPython modules. # if(POLICY CMP0094) cmake_policy (GET CMP0094 _${_PYTHON_PREFIX}_LOOKUP_POLICY) else() set (_${_PYTHON_PREFIX}_FIND_STRATEGY "LOCATION") endif() cmake_policy (VERSION 3.7) if (_${_PYTHON_PREFIX}_LOOKUP_POLICY) cmake_policy (SET CMP0094 ${_${_PYTHON_PREFIX}_LOOKUP_POLICY}) endif() # # Initial configuration # if (NOT DEFINED _PYTHON_PREFIX) message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() if (NOT DEFINED _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL 3) set(_${_PYTHON_PREFIX}_VERSIONS 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) elseif (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL 2) set(_${_PYTHON_PREFIX}_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) else() message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() get_property(_${_PYTHON_PREFIX}_CMAKE_ROLE GLOBAL PROPERTY CMAKE_ROLE) # # helper commands # macro (_PYTHON_DISPLAY_FAILURE _PYTHON_MSG) if (${_PYTHON_PREFIX}_FIND_REQUIRED) message (FATAL_ERROR "${_PYTHON_MSG}") else() if (NOT ${_PYTHON_PREFIX}_FIND_QUIETLY) message(STATUS "${_PYTHON_MSG}") endif () endif() set (${_PYTHON_PREFIX}_FOUND FALSE) string (TOUPPER "${_PYTHON_PREFIX}" _${_PYTHON_PREFIX}_UPPER_PREFIX) set (${_PYTHON_UPPER_PREFIX}_FOUND FALSE) return() endmacro() macro (_PYTHON_FIND_FRAMEWORKS) set (${_PYTHON_PREFIX}_FRAMEWORKS) if (CMAKE_HOST_APPLE OR APPLE) set (_pff_frameworks ${CMAKE_FRAMEWORK_PATH} $ENV{CMAKE_FRAMEWORK_PATH} ~/Library/Frameworks /usr/local/Frameworks ${CMAKE_SYSTEM_FRAMEWORK_PATH}) list (REMOVE_DUPLICATES _pff_frameworks) foreach (_pff_framework IN LISTS _pff_frameworks) if (EXISTS ${_pff_framework}/Python.framework) list (APPEND ${_PYTHON_PREFIX}_FRAMEWORKS ${_pff_framework}/Python.framework) endif() endforeach() unset (_pff_frameworks) unset (_pff_framework) endif() endmacro() function (_PYTHON_GET_FRAMEWORKS _PYTHON_PGF_FRAMEWORK_PATHS _PYTHON_VERSION) set (_PYTHON_FRAMEWORK_PATHS) foreach (_PYTHON_FRAMEWORK IN LISTS ${_PYTHON_PREFIX}_FRAMEWORKS) list (APPEND _PYTHON_FRAMEWORK_PATHS "${_PYTHON_FRAMEWORK}/Versions/${_PYTHON_VERSION}") endforeach() set (${_PYTHON_PGF_FRAMEWORK_PATHS} ${_PYTHON_FRAMEWORK_PATHS} PARENT_SCOPE) endfunction() function (_PYTHON_GET_REGISTRIES _PYTHON_PGR_REGISTRY_PATHS _PYTHON_VERSION) string (REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${_PYTHON_VERSION}) set (${_PYTHON_PGR_REGISTRY_PATHS} [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_PYTHON_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_PYTHON_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_PYTHON_VERSION}\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_PYTHON_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_PYTHON_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_PYTHON_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_PYTHON_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_PYTHON_VERSION}\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_PYTHON_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_PYTHON_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] PARENT_SCOPE) endfunction() function (_PYTHON_GET_PATH_SUFFIXES _PYTHON_PGPS_PATH_SUFFIXES _PYTHON_VERSION _PYTHON_TYPE) set (path_suffixes) if (_PYTHON_TYPE STREQUAL "LIBRARY") if (CMAKE_LIBRARY_ARCHITECTURE) list (APPEND path_suffixes lib/${CMAKE_LIBRARY_ARCHITECTURE}) endif() list (APPEND path_suffixes lib libs) if (CMAKE_LIBRARY_ARCHITECTURE) list (APPEND path_suffixes lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}mu-${CMAKE_LIBRARY_ARCHITECTURE} lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}m-${CMAKE_LIBRARY_ARCHITECTURE} lib/python${_PYTHON_VERSION}/config-${CMAKE_MATCH_1}u-${CMAKE_LIBRARY_ARCHITECTURE} lib/python${_PYTHON_VERSION}/config-${CMAKE_MATCH_1}-${CMAKE_LIBRARY_ARCHITECTURE}) endif() list (APPEND path_suffixes lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}mu lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}m lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}u lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION} lib/python${_PYTHON_VERSION}/config) elseif (_PYTHON_TYPE STREQUAL "INCLUDE") list (APPEND path_suffixes include/python${_PYTHON_VERSION}mu include/python${_PYTHON_VERSION}m include/python${_PYTHON_VERSION}u include/python${_PYTHON_VERSION} include) endif() set (${_PYTHON_PGPS_PATH_SUFFIXES} ${path_suffixes} PARENT_SCOPE) endfunction() function (_PYTHON_GET_LIB_NAMES _PYTHON_PGLN_NAMES _PYTHON_VERSION) string (REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${_PYTHON_VERSION}) if (ARGC EQUAL 3 AND ARGV2 STREQUAL "DEBUG") set (${_PYTHON_PGLN_NAMES} python${_PYTHON_VERSION_NO_DOTS}_d PARENT_SCOPE) else() set (${_PYTHON_PGLN_NAMES} python${_PYTHON_VERSION_NO_DOTS} python${_PYTHON_VERSION}mu python${_PYTHON_VERSION}m python${_PYTHON_VERSION}u python${_PYTHON_VERSION} PARENT_SCOPE) endif() endfunction() function (_PYTHON_VALIDATE_INTERPRETER) if (NOT ${_PYTHON_PREFIX}_EXECUTABLE) return() endif() cmake_parse_arguments (_PVI "EXACT" "" "" ${ARGN}) if (_PVI_UNPARSED_ARGUMENTS) set (expected_version ${_PVI_UNPARSED_ARGUMENTS}) else() unset (expected_version) endif() get_filename_component (python_name "${${_PYTHON_PREFIX}_EXECUTABLE}" NAME) if (expected_version AND NOT python_name STREQUAL "python${expected_version}${CMAKE_EXECUTABLE_SUFFIX}") # executable found must have a specific version execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:2]]))" RESULT_VARIABLE result OUTPUT_VARIABLE version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (result OR (_PVI_EXACT AND NOT version VERSION_EQUAL expected_version) OR (version VERSION_LESS expected_version)) # interpreter not usable or has wrong major version set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE) return() endif() else() if (NOT python_name STREQUAL "python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}${CMAKE_EXECUTABLE_SUFFIX}") # executable found do not have version in name # ensure major version is OK execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write(str(sys.version_info[0]))" RESULT_VARIABLE result OUTPUT_VARIABLE version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (result OR NOT version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # interpreter not usable or has wrong major version set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE) return() endif() endif() endif() if (CMAKE_SIZEOF_VOID_P AND "Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND NOT CMAKE_CROSSCOMPILING) # In this case, interpreter must have same architecture as environment execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))" RESULT_VARIABLE result OUTPUT_VARIABLE size ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (result OR NOT size EQUAL CMAKE_SIZEOF_VOID_P) # interpreter not usable or has wrong architecture set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE) return() endif() endif() endfunction() function (_PYTHON_VALIDATE_COMPILER expected_version) if (NOT ${_PYTHON_PREFIX}_COMPILER) return() endif() cmake_parse_arguments (_PVC "EXACT" "" "" ${ARGN}) if (_PVC_UNPARSED_ARGUMENTS) set (major_version FALSE) set (expected_version ${_PVC_UNPARSED_ARGUMENTS}) else() set (major_version TRUE) set (expected_version ${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}) set (_PVC_EXACT TRUE) endif() # retrieve python environment version from compiler set (working_dir "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir") if (major_version) # check only major version file (WRITE "${working_dir}/version.py" "import sys; sys.stdout.write(str(sys.version_info[0]))") else() file (WRITE "${working_dir}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:2]]))\n") endif() execute_process (COMMAND "${${_PYTHON_PREFIX}_COMPILER}" /target:exe /embed "${working_dir}/version.py" WORKING_DIRECTORY "${working_dir}" OUTPUT_QUIET ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process (COMMAND "${working_dir}/version" WORKING_DIRECTORY "${working_dir}" RESULT_VARIABLE result OUTPUT_VARIABLE version ERROR_QUIET) file (REMOVE_RECURSE "${_${_PYTHON_PREFIX}_VERSION_DIR}") if (result OR (_PVC_EXACT AND NOT version VERSION_EQUAL expected_version) OR (version VERSION_LESS expected_version)) # Compiler not usable or has wrong version set (${_PYTHON_PREFIX}_COMPILER ${_PYTHON_PREFIX}_COMPILER-NOTFOUND CACHE INTERNAL "" FORCE) endif() endfunction() function (_PYTHON_FIND_RUNTIME_LIBRARY _PYTHON_LIB) string (REPLACE "_RUNTIME" "" _PYTHON_LIB "${_PYTHON_LIB}") # look at runtime part on systems supporting it if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR (CMAKE_SYSTEM_NAME MATCHES "MSYS|CYGWIN" AND ${_PYTHON_LIB} MATCHES "${CMAKE_IMPORT_LIBRARY_SUFFIX}$")) set (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX}) # MSYS has a special syntax for runtime libraries if (CMAKE_SYSTEM_NAME MATCHES "MSYS") list (APPEND CMAKE_FIND_LIBRARY_PREFIXES "msys-") endif() find_library (${ARGV}) endif() endfunction() function (_PYTHON_SET_LIBRARY_DIRS _PYTHON_SLD_RESULT) unset (_PYTHON_DIRS) set (_PYTHON_LIBS ${ARGV}) list (REMOVE_AT _PYTHON_LIBS 0) foreach (_PYTHON_LIB IN LISTS _PYTHON_LIBS) if (${_PYTHON_LIB}) get_filename_component (_PYTHON_DIR "${${_PYTHON_LIB}}" DIRECTORY) list (APPEND _PYTHON_DIRS "${_PYTHON_DIR}") endif() endforeach() if (_PYTHON_DIRS) list (REMOVE_DUPLICATES _PYTHON_DIRS) endif() set (${_PYTHON_SLD_RESULT} ${_PYTHON_DIRS} PARENT_SCOPE) endfunction() # If major version is specified, it must be the same as internal major version if (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR AND NOT ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Wrong major version specified is \"${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}\", but expected major version is \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") endif() # handle components if (NOT ${_PYTHON_PREFIX}_FIND_COMPONENTS) set (${_PYTHON_PREFIX}_FIND_COMPONENTS Interpreter) set (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter TRUE) endif() if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Interpreter" "Development") list (REMOVE_DUPLICATES ${_PYTHON_PREFIX}_FIND_COMPONENTS) endif() foreach (_${_PYTHON_PREFIX}_COMPONENT IN LISTS ${_PYTHON_PREFIX}_FIND_COMPONENTS) set (${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_FOUND FALSE) endforeach() unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) # Set versions to search ## default: search any version set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSIONS}) if (${_PYTHON_PREFIX}_FIND_VERSION_COUNT GREATER 1) if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}.${${_PYTHON_PREFIX}_FIND_VERSION_MINOR}) else() unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) # add all compatible versions foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_VERSIONS) if (_${_PYTHON_PREFIX}_VERSION VERSION_GREATER_EQUAL ${_PYTHON_PREFIX}_FIND_VERSION) list (APPEND _${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSION}) endif() endforeach() endif() endif() # Define lookup strategy if (_${_PYTHON_PREFIX}_LOOKUP_POLICY STREQUAL "NEW") set (_${_PYTHON_PREFIX}_FIND_STRATEGY "LOCATION") else() set (_${_PYTHON_PREFIX}_FIND_STRATEGY "VERSION") endif() if (DEFINED ${_PYTHON_PREFIX}_FIND_STRATEGY) if (NOT ${_PYTHON_PREFIX}_FIND_STRATEGY MATCHES "^(VERSION|LOCATION)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_STRATEGY}: invalid value for '${_PYTHON_PREFIX}_FIND_STRATEGY'. 'VERSION' or 'LOCATION' expected.") set (_${_PYTHON_PREFIX}_FIND_STRATEGY "VERSION") else() set (_${_PYTHON_PREFIX}_FIND_STRATEGY "${${_PYTHON_PREFIX}_FIND_STRATEGY}") endif() endif() # Python and Anaconda distributions: define which architectures can be used if (CMAKE_SIZEOF_VOID_P) # In this case, search only for 64bit or 32bit math (EXPR _${_PYTHON_PREFIX}_ARCH "${CMAKE_SIZEOF_VOID_P} * 8") set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH}) else() # architecture unknown, search for both 64bit and 32bit set (_${_PYTHON_PREFIX}_ARCH 64) set (_${_PYTHON_PREFIX}_ARCH2 32) endif() # IronPython support if (CMAKE_SIZEOF_VOID_P) # In this case, search only for 64bit or 32bit math (EXPR _${_PYTHON_PREFIX}_ARCH "${CMAKE_SIZEOF_VOID_P} * 8") set (_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES ipy${_${_PYTHON_PREFIX}_ARCH} ipy) else() # architecture unknown, search for natural interpreter set (_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES ipy) endif() set (_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES net45 net40) # Apple frameworks handling _python_find_frameworks () set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK "FIRST") if (DEFINED ${_PYTHON_PREFIX}_FIND_FRAMEWORK) if (NOT ${_PYTHON_PREFIX}_FIND_FRAMEWORK MATCHES "^(FIRST|LAST|NEVER)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_FRAMEWORK}: invalid value for '${_PYTHON_PREFIX}_FIND_FRAMEWORK'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") else() set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK ${${_PYTHON_PREFIX}_FIND_FRAMEWORK}) endif() elseif (DEFINED CMAKE_FIND_FRAMEWORK) if (CMAKE_FIND_FRAMEWORK STREQUAL "ONLY") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: CMAKE_FIND_FRAMEWORK: 'ONLY' value is not supported. 'FIRST' will be used instead.") elseif (NOT CMAKE_FIND_FRAMEWORK MATCHES "^(FIRST|LAST|NEVER)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${CMAKE_FIND_FRAMEWORK}: invalid value for 'CMAKE_FIND_FRAMEWORK'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") else() set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) endif() endif() # Save CMAKE_FIND_APPBUNDLE if (DEFINED CMAKE_FIND_APPBUNDLE) set (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE ${CMAKE_FIND_APPBUNDLE}) else() unset (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE) endif() # To avoid app bundle lookup set (CMAKE_FIND_APPBUNDLE "NEVER") # Save CMAKE_FIND_FRAMEWORK if (DEFINED CMAKE_FIND_FRAMEWORK) set (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) else() unset (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) endif() # To avoid framework lookup set (CMAKE_FIND_FRAMEWORK "NEVER") # Windows Registry handling if (DEFINED ${_PYTHON_PREFIX}_FIND_REGISTRY) if (NOT ${_PYTHON_PREFIX}_FIND_REGISTRY MATCHES "^(FIRST|LAST|NEVER)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_REGISTRY}: invalid value for '${_PYTHON_PREFIX}_FIND_REGISTRY'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") set (_${_PYTHON_PREFIX}_FIND_REGISTRY "FIRST") else() set (_${_PYTHON_PREFIX}_FIND_REGISTRY ${${_PYTHON_PREFIX}_FIND_REGISTRY}) endif() else() set (_${_PYTHON_PREFIX}_FIND_REGISTRY "FIRST") endif() # virtual environments handling if (DEFINED ENV{VIRTUAL_ENV}) if (DEFINED ${_PYTHON_PREFIX}_FIND_VIRTUALENV) if (NOT ${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY|STANDARD)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}: invalid value for '${_PYTHON_PREFIX}_FIND_VIRTUALENV'. 'FIRST', 'ONLY' or 'STANDARD' expected. 'FIRST' will be used instead.") set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV "FIRST") else() set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}) endif() else() set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV FIRST) endif() else() set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STANDARD) endif() unset (_${_PYTHON_PREFIX}_REQUIRED_VARS) unset (_${_PYTHON_PREFIX}_CACHED_VARS) # first step, search for the interpreter if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_EXECUTABLE) if (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_EXECUTABLE) endif() set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") unset (_${_PYTHON_PREFIX}_NAMES) unset (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS) unset (_${_PYTHON_PREFIX}_REGISTRY_PATHS) foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) # build all executable names list (APPEND _${_PYTHON_PREFIX}_NAMES python${_${_PYTHON_PREFIX}_VERSION}) # Framework Paths _python_get_frameworks (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_VERSION}) list (APPEND _${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS}) # Registry Paths _python_get_registries (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_VERSION}) list (APPEND _${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS} [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]) endforeach() list (APPEND _${_PYTHON_PREFIX}_NAMES python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) while (TRUE) # Virtual environments handling if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ENV VIRTUAL_ENV PATH_SUFFIXES bin Scripts NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION}) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() if (NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") break() endif() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES bin NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION}) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION}) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endif() # try using HINTS and standard paths find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}) _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION}) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES bin NO_DEFAULT_PATH) _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION}) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_DEFAULT_PATH) _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION}) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endif() break() endwhile() else() # look-up for various versions and locations foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) set (_${_PYTHON_PREFIX}_NAMES python${_${_PYTHON_PREFIX}_VERSION} python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION}) # Virtual environments handling if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ENV VIRTUAL_ENV PATH_SUFFIXES bin Scripts NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") continue() endif() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES bin NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() # try using HINTS find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() # try using standard paths. # NAMES_PER_DIR is not defined on purpose to have a chance to find # expected version. # For example, typical systems have 'python' for version 2.* and 'python3' # for version 3.*. So looking for names per dir will find, potentially, # systematically 'python' (i.e. version 2) even if version 3 is searched. if (CMAKE_HOST_WIN32) find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES python${_${_PYTHON_PREFIX}_VERSION} python ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}) else() find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES python${_${_PYTHON_PREFIX}_VERSION}) endif() _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES bin NO_DEFAULT_PATH) endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_DEFAULT_PATH) endif() _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT) if (${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endforeach() if (NOT ${_PYTHON_PREFIX}_EXECUTABLE AND NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") # No specific version found. Retry with generic names and standard paths. # NAMES_PER_DIR is not defined on purpose to have a chance to find # expected version. # For example, typical systems have 'python' for version 2.* and 'python3' # for version 3.*. So looking for names per dir will find, potentially, # systematically 'python' (i.e. version 2) even if version 3 is searched. find_program (${_PYTHON_PREFIX}_EXECUTABLE NAMES python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}) _python_validate_interpreter () endif() endif() # retrieve exact version of executable found if (${_PYTHON_PREFIX}_EXECUTABLE) execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE ${_PYTHON_PREFIX}_VERSION ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${${_PYTHON_PREFIX}_VERSION}") list (GET _${_PYTHON_PREFIX}_VERSIONS 0 ${_PYTHON_PREFIX}_VERSION_MAJOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 1 ${_PYTHON_PREFIX}_VERSION_MINOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 2 ${_PYTHON_PREFIX}_VERSION_PATCH) else() # Interpreter is not usable set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE) unset (${_PYTHON_PREFIX}_VERSION) endif() endif() if (${_PYTHON_PREFIX}_EXECUTABLE AND ${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE) # Use interpreter version for future searches to ensure consistency set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) endif() if (${_PYTHON_PREFIX}_Interpreter_FOUND) if (NOT CMAKE_SIZEOF_VOID_P) # determine interpreter architecture execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; print(sys.maxsize > 2**32)" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE ${_PYTHON_PREFIX}_IS64BIT ERROR_VARIABLE ${_PYTHON_PREFIX}_IS64BIT) if (NOT _${_PYTHON_PREFIX}_RESULT) if (${_PYTHON_PREFIX}_IS64BIT) set (_${_PYTHON_PREFIX}_ARCH 64) set (_${_PYTHON_PREFIX}_ARCH2 64) else() set (_${_PYTHON_PREFIX}_ARCH 32) set (_${_PYTHON_PREFIX}_ARCH2 32) endif() endif() endif() # retrieve interpreter identity execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -V RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID ERROR_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID) if (NOT _${_PYTHON_PREFIX}_RESULT) if (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Anaconda") set (${_PYTHON_PREFIX}_INTERPRETER_ID "Anaconda") elseif (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Enthought") set (${_PYTHON_PREFIX}_INTERPRETER_ID "Canopy") else() string (REGEX REPLACE "^([^ ]+).*" "\\1" ${_PYTHON_PREFIX}_INTERPRETER_ID "${${_PYTHON_PREFIX}_INTERPRETER_ID}") if (${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "Python") # try to get a more precise ID execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; print(sys.copyright)" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE ${_PYTHON_PREFIX}_COPYRIGHT ERROR_QUIET) if (${_PYTHON_PREFIX}_COPYRIGHT MATCHES "ActiveState") set (${_PYTHON_PREFIX}_INTERPRETER_ID "ActivePython") endif() endif() endif() else() set (${_PYTHON_PREFIX}_INTERPRETER_ID Python) endif() else() unset (${_PYTHON_PREFIX}_INTERPRETER_ID) endif() # retrieve various package installation directories execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig;sys.stdout.write(';'.join([sysconfig.get_python_lib(plat_specific=False,standard_lib=True),sysconfig.get_python_lib(plat_specific=True,standard_lib=True),sysconfig.get_python_lib(plat_specific=False,standard_lib=False),sysconfig.get_python_lib(plat_specific=True,standard_lib=False)]))" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_LIBPATHS ERROR_QUIET) if (NOT _${_PYTHON_PREFIX}_RESULT) list (GET _${_PYTHON_PREFIX}_LIBPATHS 0 ${_PYTHON_PREFIX}_STDLIB) list (GET _${_PYTHON_PREFIX}_LIBPATHS 1 ${_PYTHON_PREFIX}_STDARCH) list (GET _${_PYTHON_PREFIX}_LIBPATHS 2 ${_PYTHON_PREFIX}_SITELIB) list (GET _${_PYTHON_PREFIX}_LIBPATHS 3 ${_PYTHON_PREFIX}_SITEARCH) else() unset (${_PYTHON_PREFIX}_STDLIB) unset (${_PYTHON_PREFIX}_STDARCH) unset (${_PYTHON_PREFIX}_SITELIB) unset (${_PYTHON_PREFIX}_SITEARCH) endif() mark_as_advanced (${_PYTHON_PREFIX}_EXECUTABLE) endif() # second step, search for compiler (IronPython) if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_COMPILER) if (${_PYTHON_PREFIX}_FIND_REQUIRED_Compiler) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_COMPILER) endif() # IronPython specific artifacts # If IronPython interpreter is found, use its path unset (_${_PYTHON_PREFIX}_IRON_ROOT) if (${_PYTHON_PREFIX}_Interpreter_FOUND AND ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") get_filename_component (_${_PYTHON_PREFIX}_IRON_ROOT "${${_PYTHON_PREFIX}_EXECUTABLE}" DIRECTORY) endif() if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") set (_${_PYTHON_PREFIX}_REGISTRY_PATHS) foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) # Registry Paths list (APPEND _${_PYTHON_PREFIX}_REGISTRY_PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]) endforeach() while (TRUE) if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_program (${_PYTHON_PREFIX}_COMPILER NAMES ipyc HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) _python_validate_compiler (${${_PYTHON_PREFIX}_FIND_VERSION}) if (${_PYTHON_PREFIX}_COMPILER) break() endif() endif() find_program (${_PYTHON_PREFIX}_COMPILER NAMES ipyc HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) _python_validate_compiler (${${_PYTHON_PREFIX}_FIND_VERSION}) if (${_PYTHON_PREFIX}_COMPILER) break() endif() if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") find_program (${_PYTHON_PREFIX}_COMPILER NAMES ipyc PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_DEFAULT_PATH) endif() break() endwhile() else() # try using root dir and registry foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_program (${_PYTHON_PREFIX}_COMPILER NAMES ipyc HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) _python_validate_compiler (${_${_PYTHON_PREFIX}_VERSION} EXACT) if (${_PYTHON_PREFIX}_COMPILER) break() endif() endif() find_program (${_PYTHON_PREFIX}_COMPILER NAMES ipyc HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) _python_validate_compiler (${_${_PYTHON_PREFIX}_VERSION} EXACT) if (${_PYTHON_PREFIX}_COMPILER) break() endif() if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") find_program (${_PYTHON_PREFIX}_COMPILER NAMES ipyc PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} NO_DEFAULT_PATH) _python_validate_compiler (${_${_PYTHON_PREFIX}_VERSION} EXACT) if (${_PYTHON_PREFIX}_COMPILER) break() endif() endif() endforeach() # no specific version found, re-try in standard paths find_program (${_PYTHON_PREFIX}_COMPILER NAMES ipyc HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}) endif() if (${_PYTHON_PREFIX}_COMPILER) # retrieve python environment version from compiler set (_${_PYTHON_PREFIX}_VERSION_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir") file (WRITE "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))\n") execute_process (COMMAND "${${_PYTHON_PREFIX}_COMPILER}" /target:exe /embed "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" WORKING_DIRECTORY "${_${_PYTHON_PREFIX}_VERSION_DIR}" OUTPUT_QUIET ERROR_QUIET) execute_process (COMMAND "${_${_PYTHON_PREFIX}_VERSION_DIR}/version" WORKING_DIRECTORY "${_${_PYTHON_PREFIX}_VERSION_DIR}" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_VERSION ERROR_QUIET) if (NOT _${_PYTHON_PREFIX}_RESULT) string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${_${_PYTHON_PREFIX}_VERSION}") list (GET _${_PYTHON_PREFIX}_VERSIONS 0 _${_PYTHON_PREFIX}_VERSION_MAJOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 1 _${_PYTHON_PREFIX}_VERSION_MINOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 2 _${_PYTHON_PREFIX}_VERSION_PATCH) if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND) # set public version information set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION}) set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR}) set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR}) set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH}) endif() else() # compiler not usable set (${_PYTHON_PREFIX}_COMPILER ${_PYTHON_PREFIX}_COMPILER-NOTFOUND CACHE INTERNAL "" FORCE) endif() file (REMOVE_RECURSE "${_${_PYTHON_PREFIX}_VERSION_DIR}") endif() if (${_PYTHON_PREFIX}_COMPILER) if (${_PYTHON_PREFIX}_Interpreter_FOUND) # Compiler must be compatible with interpreter if (${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR} VERSION_EQUAL ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) set (${_PYTHON_PREFIX}_Compiler_FOUND TRUE) endif() elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) set (${_PYTHON_PREFIX}_Compiler_FOUND TRUE) # Use compiler version for future searches to ensure consistency set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) endif() endif() if (${_PYTHON_PREFIX}_Compiler_FOUND) set (${_PYTHON_PREFIX}_COMPILER_ID IronPython) else() unset (${_PYTHON_PREFIX}_COMPILER_ID) endif() mark_as_advanced (${_PYTHON_PREFIX}_COMPILER) endif() # third step, search for the development artifacts ## Development environment is not compatible with IronPython interpreter if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND NOT ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_LIBRARY ${_PYTHON_PREFIX}_LIBRARY_RELEASE ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE ${_PYTHON_PREFIX}_LIBRARY_DEBUG ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG ${_PYTHON_PREFIX}_INCLUDE_DIR) if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARY ${_PYTHON_PREFIX}_INCLUDE_DIR) endif() # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES unset (_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES) if (DEFINED ${_PYTHON_PREFIX}_USE_STATIC_LIBS AND NOT WIN32) set(_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) if(${_PYTHON_PREFIX}_USE_STATIC_LIBS) set (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) else() list (REMOVE_ITEM CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() endif() # if python interpreter is found, use its location and version to ensure consistency # between interpreter and development environment unset (_${_PYTHON_PREFIX}_PREFIX) unset (_${_PYTHON_PREFIX}_EXEC_PREFIX) unset (_${_PYTHON_PREFIX}_BASE_EXEC_PREFIX) if (${_PYTHON_PREFIX}_Interpreter_FOUND) execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.EXEC_PREFIX)" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_EXEC_PREFIX ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) unset (_${_PYTHON_PREFIX}_EXEC_PREFIX) endif() if (NOT ${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "STANDARD") execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.BASE_EXEC_PREFIX)" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_BASE_EXEC_PREFIX ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) unset (_${_PYTHON_PREFIX}_BASE_EXEC_PREFIX) endif() endif() endif() set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_EXEC_PREFIX}" "${_${_PYTHON_PREFIX}_BASE_EXEC_PREFIX}" "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") set (_${_PYTHON_PREFIX}_CONFIG_NAMES) foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) if (DEFINED CMAKE_LIBRARY_ARCHITECTURE) list (APPEND _${_PYTHON_PREFIX}_CONFIG_NAMES "${CMAKE_LIBRARY_ARCHITECTURE}-python${_${_PYTHON_PREFIX}_VERSION}-config") endif() list (APPEND _${_PYTHON_PREFIX}_CONFIG_NAMES "python${_${_PYTHON_PREFIX}_VERSION}-config") endforeach() find_program (_${_PYTHON_PREFIX}_CONFIG NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin) if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) # check that config tool match library architecture execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) unset (_${_PYTHON_PREFIX}_CONFIG CACHE) else() string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT) if (_${_PYTHON_PREFIX}_RESULT EQUAL -1) unset (_${_PYTHON_PREFIX}_CONFIG CACHE) endif() endif() endif() else() foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) # try to use pythonX.Y-config tool set (_${_PYTHON_PREFIX}_CONFIG_NAMES) if (DEFINED CMAKE_LIBRARY_ARCHITECTURE) set (_${_PYTHON_PREFIX}_CONFIG_NAMES "${CMAKE_LIBRARY_ARCHITECTURE}-python${_${_PYTHON_PREFIX}_VERSION}-config") endif() list (APPEND _${_PYTHON_PREFIX}_CONFIG_NAMES "python${_${_PYTHON_PREFIX}_VERSION}-config") find_program (_${_PYTHON_PREFIX}_CONFIG NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin) unset (_${_PYTHON_PREFIX}_CONFIG_NAMES) if (NOT _${_PYTHON_PREFIX}_CONFIG) continue() endif() if (DEFINED CMAKE_LIBRARY_ARCHITECTURE) # check that config tool match library architecture execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) unset (_${_PYTHON_PREFIX}_CONFIG CACHE) continue() endif() string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT) if (_${_PYTHON_PREFIX}_RESULT EQUAL -1) unset (_${_PYTHON_PREFIX}_CONFIG CACHE) continue() endif() endif() if (_${_PYTHON_PREFIX}_CONFIG) break() endif() endforeach() endif() if (_${_PYTHON_PREFIX}_CONFIG) # retrieve root install directory execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --prefix RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PREFIX ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) # python-config is not usable unset (_${_PYTHON_PREFIX}_CONFIG CACHE) endif() endif() if (_${_PYTHON_PREFIX}_CONFIG) set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") unset (_${_PYTHON_PREFIX}_LIB_DIRS) unset (_${_PYTHON_PREFIX}_PATH_SUFFIXES) unset (_${_PYTHON_PREFIX}_LIB_NAMES) # retrieve library execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --ldflags RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) # retrieve library directory string (REGEX MATCHALL "-L[^ ]+" _${_PYTHON_PREFIX}_LIB_DIRS "${_${_PYTHON_PREFIX}_FLAGS}") string (REPLACE "-L" "" _${_PYTHON_PREFIX}_LIB_DIRS "${_${_PYTHON_PREFIX}_LIB_DIRS}") if (_${_PYTHON_PREFIX}_CONFIG MATCHES "python([0-9.]+)-config") _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES ${CMAKE_MATCH_1} LIBRARY) endif() # retrieve library name string (REGEX MATCHALL "-lpython[^ ]+" _${_PYTHON_PREFIX}_LIB_NAMES "${_${_PYTHON_PREFIX}_FLAGS}") string (REPLACE "-l" "" _${_PYTHON_PREFIX}_LIB_NAMES "${_${_PYTHON_PREFIX}_LIB_NAMES}") list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_LIB_NAMES) endif() execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) list (APPEND _${_PYTHON_PREFIX}_LIB_DIRS "${_${_PYTHON_PREFIX}_CONFIGDIR}") endif() list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_LIB_DIRS) list (APPEND _${_PYTHON_PREFIX}_HINTS ${_${_PYTHON_PREFIX}_LIB_DIRS}) if (NOT _${_PYTHON_PREFIX}_LIB_NAMES) # config tool do not specify "-l" option (it is the case starting with 3.8) # extract version from the config tool name and list all possible lib names if (_${_PYTHON_PREFIX}_CONFIG MATCHES "python([0-9.]+)-config") _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES ${CMAKE_MATCH_1}) endif() endif() list (APPEND _${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) # retrieve runtime library if (${_PYTHON_PREFIX}_LIBRARY_RELEASE) get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # retrieve include directory execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --includes RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) # retrieve include directory string (REGEX MATCHALL "-I[^ ]+" _${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_FLAGS}") string (REPLACE "-I" "" _${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIRS}") list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_INCLUDE_DIRS) find_path (${_PYTHON_PREFIX}_INCLUDE_DIR NAMES Python.h HINTS ${_${_PYTHON_PREFIX}_INCLUDE_DIRS} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() endif() # Rely on HINTS and standard paths if config tool failed to locate artifacts if (NOT ${_PYTHON_PREFIX}_LIBRARY_RELEASE OR NOT ${_PYTHON_PREFIX}_INCLUDE_DIR) set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") unset (_${_PYTHON_PREFIX}_LIB_NAMES) unset (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG) unset (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS) unset (_${_PYTHON_PREFIX}_REGISTRY_PATHS) unset (_${_PYTHON_PREFIX}_PATH_SUFFIXES) foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) # library names _python_get_lib_names (_${_PYTHON_PREFIX}_VERSION_NAMES ${_${_PYTHON_PREFIX}_LIB_VERSION}) list (APPEND _${_PYTHON_PREFIX}_LIB_NAMES ${_${_PYTHON_PREFIX}_VERSION_NAMES}) _python_get_lib_names (_${_PYTHON_PREFIX}_VERSION_NAMES ${_${_PYTHON_PREFIX}_LIB_VERSION} DEBUG) list (APPEND _${_PYTHON_PREFIX}_LIB_NAMES_DEBUG ${_${_PYTHON_PREFIX}_VERSION_NAMES}) # Framework Paths _python_get_frameworks (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION}) list (APPEND _${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS}) # Registry Paths _python_get_registries (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION}) list (APPEND _${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS}) # Paths suffixes _python_get_path_suffixes (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY) list (APPEND _${_PYTHON_PREFIX}_PATH_SUFFIXES ${_${_PYTHON_PREFIX}_VERSION_PATHS}) endforeach() if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search in HINTS locations find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) else() unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) else() unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) endif() # search in all default paths find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) if (${_PYTHON_PREFIX}_LIBRARY_RELEASE) # extract version from library name if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "python([23])([0-9]+)") set (_${_PYTHON_PREFIX}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}") elseif (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "python([23])\\.([0-9]+)") set (_${_PYTHON_PREFIX}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}") endif() endif() if (WIN32) # search for debug library if (${_PYTHON_PREFIX}_LIBRARY_RELEASE) # use library location as a hint _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG ${_${_PYTHON_PREFIX}_VERSION} DEBUG) get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} NO_DEFAULT_PATH) else() # search first in known locations if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES lib libs NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search in all default paths find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES lib libs) # extract version from library name if (${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "python([23])([0-9]+)") set (_${_PYTHON_PREFIX}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}") elseif (${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "python([23])\\.([0-9]+)") set (_${_PYTHON_PREFIX}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}") endif() endif() endif() else() foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES ${_${_PYTHON_PREFIX}_LIB_VERSION}) _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG ${_${_PYTHON_PREFIX}_LIB_VERSION} DEBUG) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION}) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search in HINTS locations find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) else() unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) else() unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) endif() # search in all default paths find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) if (WIN32) # search for debug library if (${_PYTHON_PREFIX}_LIBRARY_RELEASE) # use library location as a hint get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} NO_DEFAULT_PATH) else() # search first in known locations if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES lib libs NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search in all default paths find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES lib libs) endif() endif() if (${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG) set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) break() endif() endforeach() endif() # retrieve runtime libraries if (${_PYTHON_PREFIX}_LIBRARY_RELEASE) _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES ${_${_PYTHON_PREFIX}_VERSION}) get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin) endif() if (${_PYTHON_PREFIX}_LIBRARY_DEBUG) _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG ${_${_PYTHON_PREFIX}_VERSION} DEBUG) get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin) endif() # Don't search for include dir if no library was founded if (${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG) unset (_${_PYTHON_PREFIX}_INCLUDE_HINTS) if (${_PYTHON_PREFIX}_EXECUTABLE) # pick up include directory from configuration execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; import sysconfig; sys.stdout.write(sysconfig.get_path('include'))" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PATH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) file (TO_CMAKE_PATH "${_${_PYTHON_PREFIX}_PATH}" _${_PYTHON_PREFIX}_PATH) list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PATH}") endif() endif() foreach (_${_PYTHON_PREFIX}_LIB IN ITEMS ${_PYTHON_PREFIX}_LIBRARY_RELEASE ${_PYTHON_PREFIX}_LIBRARY_DEBUG) if (${_${_PYTHON_PREFIX}_LIB}) # Use the library's install prefix as a hint if (${_${_PYTHON_PREFIX}_LIB} MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") elseif (${_${_PYTHON_PREFIX}_LIB} MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_LIB} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") else() # assume library is in a directory under root get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${${_${_PYTHON_PREFIX}_LIB}}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY) list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") endif() endif() endforeach() list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_INCLUDE_HINTS) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION}) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES ${_${_PYTHON_PREFIX}_VERSION} INCLUDE) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_path (${_PYTHON_PREFIX}_INCLUDE_DIR NAMES Python.h HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_path (${_PYTHON_PREFIX}_INCLUDE_DIR NAMES Python.h HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) else() unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) else() unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) endif() find_path (${_PYTHON_PREFIX}_INCLUDE_DIR NAMES Python.h HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search header file in standard locations find_path (${_PYTHON_PREFIX}_INCLUDE_DIR NAMES Python.h) endif() if (${_PYTHON_PREFIX}_INCLUDE_DIR) # retrieve version from header file file (STRINGS "${${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" _${_PYTHON_PREFIX}_VERSION REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"") string (REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"]+)\".*" "\\1" _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_VERSION}") string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${_${_PYTHON_PREFIX}_VERSION}") list (GET _${_PYTHON_PREFIX}_VERSIONS 0 _${_PYTHON_PREFIX}_VERSION_MAJOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 1 _${_PYTHON_PREFIX}_VERSION_MINOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 2 _${_PYTHON_PREFIX}_VERSION_PATCH) if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT ${_PYTHON_PREFIX}_Compiler_FOUND) # set public version information set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION}) set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR}) set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR}) set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH}) endif() endif() # define public variables include (${CMAKE_CURRENT_LIST_DIR}/../SelectLibraryConfigurations.cmake) select_library_configurations (${_PYTHON_PREFIX}) if (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") elseif (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") else() set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "$${_PYTHON_PREFIX}_RUNTIME_LIBRARY-NOTFOUND") endif() _python_set_library_dirs (${_PYTHON_PREFIX}_LIBRARY_DIRS ${_PYTHON_PREFIX}_LIBRARY_RELEASE ${_PYTHON_PREFIX}_LIBRARY_DEBUG) if (UNIX) if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" OR ${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS ${${_PYTHON_PREFIX}_LIBRARY_DIRS}) endif() else() _python_set_library_dirs (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) endif() set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${${_PYTHON_PREFIX}_INCLUDE_DIR}") mark_as_advanced (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG ${_PYTHON_PREFIX}_INCLUDE_DIR) if ((${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG) AND ${_PYTHON_PREFIX}_INCLUDE_DIR) if (${_PYTHON_PREFIX}_Interpreter_FOUND OR ${_PYTHON_PREFIX}_Compiler_FOUND) # development environment must be compatible with interpreter/compiler if (${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR} VERSION_EQUAL ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) set (${_PYTHON_PREFIX}_Development_FOUND TRUE) endif() elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) set (${_PYTHON_PREFIX}_Development_FOUND TRUE) endif() endif() # Restore the original find library ordering if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES) set (CMAKE_FIND_LIBRARY_SUFFIXES ${_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES}) endif() endif() if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Interpreter_FOUND) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) if (${_PYTHON_PREFIX}_FIND_REQUIRED_NumPy) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) endif() execute_process( COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "from __future__ import print_function\ntry: import numpy; print(numpy.get_include(), end='')\nexcept:pass\n" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_PATH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) find_path(${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR NAMES "numpy/arrayobject.h" "numpy/numpyconfig.h" HINTS "${_${_PYTHON_PREFIX}_NumPy_PATH}" NO_DEFAULT_PATH) endif() if(${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) set(${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") set(${_PYTHON_PREFIX}_NumPy_FOUND TRUE) endif() if(${_PYTHON_PREFIX}_NumPy_FOUND) execute_process( COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "from __future__ import print_function\ntry: import numpy; print(numpy.__version__, end='')\nexcept:pass\n" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_VERSION) if (NOT _${_PYTHON_PREFIX}_RESULT) set(${_PYTHON_PREFIX}_NumPy_VERSION "${_${_PYTHON_PREFIX}_NumPy_VERSION}") endif() endif() # final step: set NumPy founded only if Development component is founded as well if (NOT ${_PYTHON_PREFIX}_Development_FOUND) set(${_PYTHON_PREFIX}_NumPy_FOUND FALSE) endif() endif() # final validation if (${_PYTHON_PREFIX}_VERSION_MAJOR AND NOT ${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Found unsuitable major version \"${${_PYTHON_PREFIX}_VERSION_MAJOR}\", but required major version is exact version \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") endif() include (${CMAKE_CURRENT_LIST_DIR}/../FindPackageHandleStandardArgs.cmake) find_package_handle_standard_args (${_PYTHON_PREFIX} REQUIRED_VARS ${_${_PYTHON_PREFIX}_REQUIRED_VARS} VERSION_VAR ${_PYTHON_PREFIX}_VERSION HANDLE_COMPONENTS) # Create imported targets and helper functions if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT TARGET ${_PYTHON_PREFIX}::Interpreter) add_executable (${_PYTHON_PREFIX}::Interpreter IMPORTED) set_property (TARGET ${_PYTHON_PREFIX}::Interpreter PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE}") endif() if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Compiler_FOUND AND NOT TARGET ${_PYTHON_PREFIX}::Compiler) add_executable (${_PYTHON_PREFIX}::Compiler IMPORTED) set_property (TARGET ${_PYTHON_PREFIX}::Compiler PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_COMPILER}") endif() if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Development_FOUND) macro (__PYTHON_IMPORT_LIBRARY __name) if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) set (_${_PYTHON_PREFIX}_LIBRARY_TYPE SHARED) else() set (_${_PYTHON_PREFIX}_LIBRARY_TYPE STATIC) endif() add_library (${__name} ${_${_PYTHON_PREFIX}_LIBRARY_TYPE} IMPORTED) set_property (TARGET ${__name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIR}") if ((${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) OR (${_PYTHON_PREFIX}_LIBRARY_DEBUG AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG)) # System manage shared libraries in two parts: import and runtime if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" IMPORTED_IMPLIB_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" IMPORTED_IMPLIB_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}" IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") else() set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_IMPLIB "${${_PYTHON_PREFIX}_LIBRARY}" IMPORTED_LOCATION "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY}") endif() else() if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) set_property (TARGET ${_PYTHON_PREFIX}::Python PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}") set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}") else() set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${${_PYTHON_PREFIX}_LIBRARY}") endif() endif() if (_${_PYTHON_PREFIX}_CONFIG AND _${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "STATIC") # extend link information with dependent libraries execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --ldflags RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) string (REGEX MATCHALL "-[Ll][^ ]+" _${_PYTHON_PREFIX}_LINK_LIBRARIES "${_${_PYTHON_PREFIX}_FLAGS}") # remove elements relative to python library itself list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-lpython") foreach (_${_PYTHON_PREFIX}_DIR IN LISTS ${_PYTHON_PREFIX}_LIBRARY_DIRS) list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-L${${_PYTHON_PREFIX}_DIR}") endforeach() set_property (TARGET ${__name} PROPERTY INTERFACE_LINK_LIBRARIES ${_${_PYTHON_PREFIX}_LINK_LIBRARIES}) endif() endif() endmacro() if (NOT TARGET ${_PYTHON_PREFIX}::Python) __python_import_library (${_PYTHON_PREFIX}::Python) endif() if (NOT TARGET ${_PYTHON_PREFIX}::Module) if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$") # On Windows/CYGWIN/MSYS, Python::Module is the same as Python::Python # but ALIAS cannot be used because the imported library is not GLOBAL. __python_import_library (${_PYTHON_PREFIX}::Module) else() add_library (${_PYTHON_PREFIX}::Module INTERFACE IMPORTED) set_property (TARGET ${_PYTHON_PREFIX}::Module PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIR}") # When available, enforce shared library generation with undefined symbols if (APPLE) set_property (TARGET ${_PYTHON_PREFIX}::Module PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup") endif() if (CMAKE_SYSTEM_NAME STREQUAL "SunOS") set_property (TARGET ${_PYTHON_PREFIX}::Module PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-z,nodefs") endif() if (CMAKE_SYSTEM_NAME STREQUAL "AIX") set_property (TARGET ${_PYTHON_PREFIX}::Module PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-b,erok") endif() endif() endif() # # PYTHON_ADD_LIBRARY ( [STATIC|SHARED|MODULE] src1 src2 ... srcN) # It is used to build modules for python. # function (__${_PYTHON_PREFIX}_ADD_LIBRARY prefix name) cmake_parse_arguments (PARSE_ARGV 2 PYTHON_ADD_LIBRARY "STATIC;SHARED;MODULE" "" "") unset (type) if (NOT (PYTHON_ADD_LIBRARY_STATIC OR PYTHON_ADD_LIBRARY_SHARED OR PYTHON_ADD_LIBRARY_MODULE)) set (type MODULE) endif() add_library (${name} ${type} ${ARGN}) get_property (type TARGET ${name} PROPERTY TYPE) if (type STREQUAL "MODULE_LIBRARY") target_link_libraries (${name} PRIVATE ${prefix}::Module) # customize library name to follow module name rules set_property (TARGET ${name} PROPERTY PREFIX "") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_property (TARGET ${name} PROPERTY SUFFIX ".pyd") endif() else() target_link_libraries (${name} PRIVATE ${prefix}::Python) endif() endfunction() endif() if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_NumPy_FOUND AND NOT TARGET ${_PYTHON_PREFIX}::NumPy AND TARGET ${_PYTHON_PREFIX}::Module) add_library (${_PYTHON_PREFIX}::NumPy INTERFACE IMPORTED) set_property (TARGET ${_PYTHON_PREFIX}::NumPy PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") target_link_libraries (${_PYTHON_PREFIX}::NumPy INTERFACE ${_PYTHON_PREFIX}::Module) endif() endif() # final clean-up # Restore CMAKE_FIND_APPBUNDLE if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE) set (CMAKE_FIND_APPBUNDLE ${_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE}) unset (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE) else() unset (CMAKE_FIND_APPBUNDLE) endif() # Restore CMAKE_FIND_FRAMEWORK if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) set (CMAKE_FIND_FRAMEWORK ${_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK}) unset (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) else() unset (CMAKE_FIND_FRAMEWORK) endif() unset (_${_PYTHON_PREFIX}_CONFIG CACHE) ledger-3.3.2/cmake/python-backport/SelectLibraryConfigurations.cmake000066400000000000000000000063321441123640000256450ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: SelectLibraryConfigurations --------------------------- .. code-block:: cmake select_library_configurations(basename) This macro takes a library base name as an argument, and will choose good values for the variables :: basename_LIBRARY basename_LIBRARIES basename_LIBRARY_DEBUG basename_LIBRARY_RELEASE depending on what has been found and set. If only ``basename_LIBRARY_RELEASE`` is defined, ``basename_LIBRARY`` will be set to the release value, and ``basename_LIBRARY_DEBUG`` will be set to ``basename_LIBRARY_DEBUG-NOTFOUND``. If only ``basename_LIBRARY_DEBUG`` is defined, then ``basename_LIBRARY`` will take the debug value, and ``basename_LIBRARY_RELEASE`` will be set to ``basename_LIBRARY_RELEASE-NOTFOUND``. If the generator supports configuration types, then ``basename_LIBRARY`` and ``basename_LIBRARIES`` will be set with debug and optimized flags specifying the library to be used for the given configuration. If no build type has been set or the generator in use does not support configuration types, then ``basename_LIBRARY`` and ``basename_LIBRARIES`` will take only the release value, or the debug value if the release one is not set. #]=======================================================================] # This macro was adapted from the FindQt4 CMake module and is maintained by Will # Dicharry . macro(select_library_configurations basename) if(NOT ${basename}_LIBRARY_RELEASE) set(${basename}_LIBRARY_RELEASE "${basename}_LIBRARY_RELEASE-NOTFOUND" CACHE FILEPATH "Path to a library.") endif() if(NOT ${basename}_LIBRARY_DEBUG) set(${basename}_LIBRARY_DEBUG "${basename}_LIBRARY_DEBUG-NOTFOUND" CACHE FILEPATH "Path to a library.") endif() get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if( ${basename}_LIBRARY_DEBUG AND ${basename}_LIBRARY_RELEASE AND NOT ${basename}_LIBRARY_DEBUG STREQUAL ${basename}_LIBRARY_RELEASE AND ( _isMultiConfig OR CMAKE_BUILD_TYPE ) ) # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for # single-config generators, set optimized and debug libraries set( ${basename}_LIBRARY "" ) foreach( _libname IN LISTS ${basename}_LIBRARY_RELEASE ) list( APPEND ${basename}_LIBRARY optimized "${_libname}" ) endforeach() foreach( _libname IN LISTS ${basename}_LIBRARY_DEBUG ) list( APPEND ${basename}_LIBRARY debug "${_libname}" ) endforeach() elseif( ${basename}_LIBRARY_RELEASE ) set( ${basename}_LIBRARY ${${basename}_LIBRARY_RELEASE} ) elseif( ${basename}_LIBRARY_DEBUG ) set( ${basename}_LIBRARY ${${basename}_LIBRARY_DEBUG} ) else() set( ${basename}_LIBRARY "${basename}_LIBRARY-NOTFOUND") endif() set( ${basename}_LIBRARIES "${${basename}_LIBRARY}" ) if( ${basename}_LIBRARY ) set( ${basename}_FOUND TRUE ) endif() mark_as_advanced( ${basename}_LIBRARY_RELEASE ${basename}_LIBRARY_DEBUG ) endmacro() ledger-3.3.2/contrib/000077500000000000000000000000001441123640000144145ustar00rootroot00000000000000ledger-3.3.2/contrib/README000066400000000000000000000002061441123640000152720ustar00rootroot00000000000000This scripts are provided just to give some ideas. They probably need to be modified to better suit your environment. Beware! John ledger-3.3.2/contrib/bal000077500000000000000000000005471441123640000151060ustar00rootroot00000000000000#!/bin/sh switch="-c" limit="-t (/Liabilities/?a<0:Ua>100)&a" if [ "$1" = "-C" -o "$1" = "-U" ]; then switch="$1" shift elif [ "$1" = "-b" -o "$1" = "-e" -o "$1" = "-p" ]; then switch="$1 $2" shift 2 fi accts="$@" if [ -z "$accts" ]; then accts="-Equity -Income -Expenses" else limit="" fi ledger -VQ $switch $limit -s -S "-UT" balance $accts ledger-3.3.2/contrib/bal-huquq000077500000000000000000000006051441123640000162420ustar00rootroot00000000000000#!/bin/sh switch="-c" limit="-t (/Liabilities/?(/Huquq/?a/P{2.22AU}<={-1.0}:a<0):Ua>100)&a" if [ "$1" = "-C" -o "$1" = "-U" ]; then switch="$1" shift elif [ "$1" = "-b" -o "$1" = "-e" -o "$1" = "-p" ]; then switch="$1 $2" shift 2 fi accts="$@" if [ -z "$accts" ]; then accts="-Equity -Income -Expenses" else limit="" fi ledger -VQ $switch $limit -s -S "-UT" balance $accts ledger-3.3.2/contrib/compilation-ledger.el000066400000000000000000000063711441123640000205230ustar00rootroot00000000000000;;; compilation-ledger.el --- error regexps for ledger ;; Copyright 2009, 2010, 2011 Kevin Ryde ;; Author: Kevin Ryde ;; Version: 1 ;; Keywords: processes ;; URL: http://user42.tuxfamily.org/compilation-ledger/index.html ;; EmacsWiki: CompilationMode ;; compilation-ledger.el is free software; you can redistribute it ;; and/or modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation; either version 3, or (at your ;; option) any later version. ;; ;; compilation-ledger.el is distributed in the hope that it will be ;; useful, but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General ;; Public License for more details. ;; ;; You can get a copy of the GNU General Public License online at ;; . ;;; Commentary: ;; This spot of code adds a `compilation-error-regexp-alist' pattern to ;; recognise error messages from the "ledger" program, ;; ;; http://newartisans.com/software/ledger.html ;; ;; such as ;; ;; Error: "foo.ledger", line 1656: Invalid date string: foo ;; ;; This is only for running ledger from M-x compile. ledger.el shows ;; reports with its own `ledger-report-mode' which has more features and ;; isn't based on `compilation-mode'. ;;; Install: ;; Put compilation-ledger.el in one of your `load-path' directories, ;; and in your .emacs add ;; ;; (eval-after-load "compile" '(require 'compilation-ledger)) ;; ;; There's an autoload cookie below for this, if you know how to use ;; `update-file-autoloads' and friends. ;;; Emacsen: ;; Designed for Emacs 20 up, works in XEmacs 21 too. ;;; History: ;; Version 1 - the first version ;;; Code: ;;;###autoload (eval-after-load "compile" '(require 'compilation-ledger)) (require 'compile) (let ((symbol 'compilation-ledger) (pattern '("^Error: \"\\([^\"\n]+?\\)\", line \\([0-9]+\\):" 1 2))) (cond ((eval-when-compile (boundp 'compilation-error-regexp-systems-list)) ;; xemacs21 (add-to-list 'compilation-error-regexp-alist-alist (list symbol pattern)) (compilation-build-compilation-error-regexp-alist)) ((eval-when-compile (boundp 'compilation-error-regexp-alist-alist)) ;; emacs22 up (add-to-list 'compilation-error-regexp-alist symbol) (add-to-list 'compilation-error-regexp-alist-alist (cons symbol pattern))) (t ;; emacs21 (add-to-list 'compilation-error-regexp-alist pattern)))) (defun compilation-ledger-unload-function () "Remove compilation-ledger regexps on `unload-feature'." (setq compilation-error-regexp-alist (remove 'compilation-ledger compilation-error-regexp-alist)) (setq compilation-error-regexp-alist-alist (remove (assq 'compilation-ledger compilation-error-regexp-alist-alist) compilation-error-regexp-alist-alist)) (when (eval-when-compile (fboundp 'compilation-build-compilation-error-regexp-alist)) (compilation-build-compilation-error-regexp-alist)) nil) ;; and normal unload-feature actions ;; LocalWords: http newartisans html el (provide 'compilation-ledger) ;;; compilation-ledger.el ends here ledger-3.3.2/contrib/entry000077500000000000000000000004331441123640000155030ustar00rootroot00000000000000#!/bin/sh if [ -z "$LEDGER" -o ! -r "$LEDGER" ]; then echo Please set your LEDGER environment variable. fi line=`wc -l $LEDGER | awk '{print $1}'` if ledger xact "$@" > /tmp/xact; then cat /tmp/xact >> $LEDGER else echo "$@" >> $LEDGER fi rm /tmp/xact vi +$line $LEDGER ledger-3.3.2/contrib/getquote-uk.py000077500000000000000000000007601441123640000172460ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import urllib, string, sys def download(sym): url = "http://uk.old.finance.yahoo.com/d/quotes.csv?s=" url += sym + "&f=sl1d1t1c1ohgv&e=.csv" f = urllib.urlopen(url, proxies={}) info = f.read() f.close() fields = string.split(info, ',') result = float(fields[1])/100 return result sym = sys.argv[1] sym = sym.replace('_', '.') if sym == '£': print '£1.00' else: try: print "£" +str(download(sym)) except: pass ledger-3.3.2/contrib/getquote.pl000077500000000000000000000006401441123640000166110ustar00rootroot00000000000000#!/usr/bin/env perl $timeout = 60; use Finance::Quote; use POSIX qw(strftime localtime time); $q = Finance::Quote->new; $q->timeout($timeout); $q->require_labels(qw/price/); %quotes = $q->fetch("nasdaq", $ARGV[0]); if ($quotes{$ARGV[0], "price"}) { print strftime('%Y/%m/%d %H:%M:%S', localtime(time())); print " ", $ARGV[0], " "; print "\$", $quotes{$ARGV[0], "price"}, "\n"; } else { exit 1; } ledger-3.3.2/contrib/html/000077500000000000000000000000001441123640000153605ustar00rootroot00000000000000ledger-3.3.2/contrib/html/README000066400000000000000000000006121441123640000162370ustar00rootroot00000000000000ledger-cli xml2html XLST This XLST produces a browsable html page out of the xml files created by ledger xml. It might be used in the future to create a ledger html output. usage: from existing xml file: xsltproc --output output-ledger.html ledger.xsl input-ledger.xml from ledger file directly: ledger -f input.ledger xml | xsltproc --output output-ledger.html ledger.xsl - ledger-3.3.2/contrib/html/ledger.xsl000066400000000000000000000207371441123640000173630ustar00rootroot00000000000000

Accounts

Account Amount Total
padding-left:em; #account-

All Transactions

Date Payee Account Amount Total
#account-
amount amount-positive amount-negative    
ledger-3.3.2/contrib/iso4127-commodities/000077500000000000000000000000001441123640000200365ustar00rootroot00000000000000ledger-3.3.2/contrib/iso4127-commodities/iso4217ledger.sh000077500000000000000000000037731441123640000227020ustar00rootroot00000000000000#!/bin/sh # iso4217ledger.sh - Convert ISO 4217 currencies to ledger commodities # # This script will download the latest XML for ISO 4217 Table A.1 # and print the contained currency & funds code list as ledger # commodity definitions to stdout. # Copyright (c) 2014 Alexis Hildebrandt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. xml_url="https://www.currency-iso.org/dam/downloads/lists/list_one.xml" xsl_file="$(dirname $0)/iso4217ledger.xsl" xsltproc="$(which xsltproc)" if [ ! -f "$xsltproc" -o ! -x "$xsltproc" ]; then echo "Can't find xsltproc" exit 1 fi download_command="$(which curl)" if [ -f "$download_command" \ -a -x "$download_command" ]; then download_options="--silent" else download_command="$(which wget)" if [ -n "$download_command" \ -a -f "$download_command" \ -a -x "$download_command" ]; then download_options="--quiet --output-document -" else echo "Can't find curl or wget." exit 1 fi fi $download_command $download_options "$xml_url" | $xsltproc "$xsl_file" - ledger-3.3.2/contrib/iso4127-commodities/iso4217ledger.xsl000066400000000000000000000077741441123640000231000ustar00rootroot00000000000000 , ; Ledger commodity declarations ; Generated from ISO 4217 Table A.1 XML ( ) using iso4217ledger.xsl ¤ commodity note - ( ) format 0000 nomarket 0 ledger-3.3.2/contrib/ledger-completion.bash000066400000000000000000000105311441123640000206640ustar00rootroot00000000000000### Assumption # # bash-completion package is installed and enabled # ### Just want to try it? # # $ source ledger-completion.bash # ### How to install? # #### For local user # # $ cat <>~/.bash_completion # . ~/.bash_completion.d/ledger # EOF # # $ cp ledger-completion.bash ~/.bash_completion.d/ledger # #### For all users # # $ sudo cp ledger-completion.bash /etc/bash_completion.d/ledger # _ledger() { local cur prev commands options COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" # COMMANDS # # Commands are found in source code: # report.cc::lookup "case symbol_t::COMMAND" # report.cc::lookupcase "case symbol_t::PRECOMMAND" : these are debug commands and they have been filtered out here # commands="accounts balance budget cleared commodities convert csv draft echo emacs entry equity lisp org payees pricemap prices pricesdb print register reload select source stats tags xact xml" # OPTIONS # # Options are found in source code: # global.cc::lookup_option # report.cc::lookup_option # session.cc::lookup_option # options="--abbrev-len= --account-width= --account= --actual --actual-dates --add-budget --amount-data --amount-width= --amount= --anon --ansi --args-only --auto-match --aux-date --average --balance-format= --base --basis --begin= --bold-if= --budget --budget-format= --by-payee --cache= --change --check-payees --cleared --cleared-format= --collapse --collapse-if-zero --color --columns= --cost --count --csv-format= --current --daily --date-format= --date-width= --date= --datetime-format= --day-break --days-of-week --dc --debug= --decimal-comma --depth= --detail --deviation --display-amount= --display-total= --display= --dow --download --effective --empty --end= --equity --exact --exchange= --explicit --file= --first= --flat --force-color --force-pager --forecast-while= --forecast-years= --forecast= --format= --full-help --gain --generated --group-by= --group-title-format= --head= --help --help-calc --help-comm --help-disp --historical --immediate --init-file= --inject= --input-date-format= --invert --last= --leeway= --limit= --lot-dates --lot-notes --lot-prices --lot-tags --lots --lots-actual --market --master-account= --meta-width= --meta= --monthly --no-aliases --no-color --no-pager --no-rounding --no-titles --no-total --now= --only= --options --output= --pager= --payee-width= --payee= --pedantic --pending --percent --period-sort= --period= --permissive --pivot= --plot-amount-format= --plot-total-format= --prepend-format= --prepend-width= --price --price-db= --price-exp= --pricedb-format= --prices-format= --primary-date --quantity --quarterly --raw --real --recursive-aliases --register-format= --related --related-all --revalued --revalued-only --revalued-total= --rich-data --script= --seed= --sort-all= --sort-xacts= --sort= --start-of-week= --strict --subtotal --tail= --time-colon --time-report --total-data --total-width= --total= --trace= --truncate= --unbudgeted --uncleared --unrealized --unrealized-gains= --unrealized-losses= --unround --value --value-expr= --values --verbose --verify --verify-memory --version --weekly --wide --yearly" # Bash FAQ E13 http://tiswww.case.edu/php/chet/bash/FAQ # COMP_WORDBREAKS=${COMP_WORDBREAKS//:} # ACCOUNTS # # Accounts are generated with bash command: # $ ledger accounts>/tmp/accounts; for i in {1..5}; do cut -d : -f $i- /tmp/accounts;cut -d : -f -$i /tmp/accounts; done|sort -u|xargs # # Warning: this is working badly if there are spaces in account names # accounts="Assets Liabilities Equity Revenue Expenses" case $prev in --@(cache|file|init-file|output|pager|price-db|script)|-@(f|i|o)) _filedir return 0 ;; @(balance|equity|print|register)) COMPREPLY=( $(compgen -W "${accounts}" -- ${cur}) ) return 0 ;; esac if [[ ${cur} == -* ]] ; then COMPREPLY=( $(compgen -W "${options}" -- ${cur}) ) # elif [[ ${cur} == [A-Z]* ]] ; then # COMPREPLY=( $(compgen -W "${accounts}" -- ${cur}) ) else COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) ) fi return 0 } complete -F _ledger ledger # Local variables: # mode: shell-script # sh-basic-offset: 4 # sh-indent-comment: t # indent-tabs-mode: nil # End: # ex: ts=4 sw=4 et filetype=sh ledger-3.3.2/contrib/ledger-du000077500000000000000000000016661441123640000162230ustar00rootroot00000000000000#!/usr/bin/env python import string import sys import os import time from stat import * from os.path import * def report_file(path): dir_elems = string.split(dirname(path), os.sep) if dir_elems[0] == "." or dir_elems[0] == "": dir_elems = dir_elems[1 :] account = string.join(dir_elems, ":") info = os.stat(path) print time.strftime("%Y/%m/%d", time.localtime(info[ST_MTIME])), print basename(path) print " ", account, " ", info[ST_SIZE], "b" print " Equity:Files" print def find_files(path): xacts = os.listdir(path) for xact in xacts: xact = join(path, xact) if not islink(xact): if isdir(xact) and xact != "/proc": find_files(xact) else: report_file(xact) args = sys.argv[1:] if len(args): paths = args else: paths = ["."] print """ C 1.00 Kb = 1024 b C 1.00 Mb = 1024 Kb C 1.00 Gb = 1024 Mb C 1.00 Tb = 1024 Gb """ for path in paths: find_files(path) ledger-3.3.2/contrib/non-profit-audit-reports/000077500000000000000000000000001441123640000213075ustar00rootroot00000000000000ledger-3.3.2/contrib/non-profit-audit-reports/GPLv3000066400000000000000000001045131441123640000221310ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ledger-3.3.2/contrib/non-profit-audit-reports/LICENSE000066400000000000000000000012771441123640000223230ustar00rootroot00000000000000Contents under contrib/non-profit-audit-reports/ are licensed GPLv3-or-later. The GPLv3-or-later licensing of the contents of this directory does not, to our knowledge and belief, impact the licensing of any other part of Ledger. Parts of the files herein are likely derivative works of the rest of Ledger, but these works are under this subdirectory are not, to our knowledge, used, imported, included, copied, etc. into other parts of the codebase. In short, this is a small application written to use Ledger like a library, particularly via its Python API interface. It derives from Ledger, but Ledger does not, to our knowledge, derive from it. We are not lawyers and this is not legal advice. ledger-3.3.2/contrib/non-profit-audit-reports/README000066400000000000000000000054261441123640000221760ustar00rootroot00000000000000README This document provides background on the enclosed example Demo ---- To run the demo do ./demo.sh Which should generate the following files in tests/ chart-of-accounts.txt general-ledger.txt general-ledger.csv general-ledger.ods And a final, "portable" zip file with the spreadsheet in general-ledger.zip It *should* be possible to copy general-ledger.zip to another system, unzip it, open general-ledger.ods in Libre Office and have the relative links resolve correctly. NOTE: Export to PDF should also work. Known Dependencies ------------------ ledger (3.0) python (2.x) zip libdate-manip-perl libmath-gmp-perl Temporary Hacks --------------- Due to an urgent project deadline the ooolib2 directory represents some fixes to: http://ooolib.sourceforge.net/ The proper version of this library can be installed on Debian systems with # apt-get install python-ooolib Compare the deltas to the current version with # diff -u /usr/share/pyshared/ooolib/__init__.py ooolib2/__init__.py Note also that the csv2ods.py treats columns 4 and 5 (numbering from 1) of the csv magically. If column 4 contains a non-empty string which is not 'Receipt' then it is interpreted as a relative path of an artifact to link to. Similarly for column 5 and 'Invoice'. Sample PDF files ---------------- The sample PDF files were created as follows: paps --font="Courier 12" --paper letter --top-margin=18 tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.txt | ps2pdf - tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf paps --font="Courier 12" --paper letter --top-margin=18 tests/Financial/Invoices/Invoice20110510.txt | ps2pdf - tests/Financial/Invoices/Invoice20110510.pdf Resources --------- ooolib http://ooolib.sourceforge.net/ LIBPF probably does not replace ooolib http://wp.libpf.com/?p=82 Libre Office Calc Guide (contains function reference) https://www.libreoffice.org/get-help/documentation/ Libre Office API http://api.libreoffice.org/examples/examples.html http://api.libreoffice.org/examples/DevelopersGuide/examples.html OpenOffice Developers Guide http://wiki.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide Spreadsheet Documents http://wiki.openoffice.org/wiki/Documentation/DevGuide/Spreadsheets/Spreadsheet_Documents How to correctly create ODF documents using zip (Do NOT do this, use ooolib instead) http://www.jejik.com/articles/2010/03/how_to_correctly_create_odf_documents_using_zip/ Line Breaks fo:break-before="page" http://books.evc-cit.info/oobook/ch03.html#page-content-section ODF Validator http://opendocumentfellowship.com/validator Editing Hyperlinks http://help.libreoffice.org/Common/Editing_Hyperlinks Perl OODoc NOTE: a replacement for POD, not ooolib http://search.cpan.org/dist/OpenOffice-OODoc/ ledger-3.3.2/contrib/non-profit-audit-reports/bank-reconcilation.plx000077500000000000000000000171341441123640000256070ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Math::BigFloat; use Date::Manip; use Data::PowerSet; Math::BigFloat->precision(-2); my $ZERO = Math::BigFloat->new("0.00"); my $ONE_HUNDRED = Math::BigFloat->new("100.00"); my $VERBOSE = 1; my $DEBUG = 0; my $LEDGER_BIN = "/usr/local/bin/ledger"; ###################################################################### sub BruteForceSubSetSumSolver ($$$) { my($numberList, $totalSought, $extractNumber) = @_; my($P, $N) = (0, 0); my $size = scalar(@{$numberList}); my %Q; my(@L) = map { { val => &$extractNumber($_), obj => $_ } } @{$numberList}; my $powerset = Data::PowerSet->new(@L); while (my $set = $powerset->next) { my $total = $ZERO; foreach my $ee (@{$set}) { $total += $ee->{val}; } if ($totalSought == $total) { my(@list) = map { $_->{obj} } @{$set}; return (1, \@list); } } return (0, []); } ###################################################################### sub DynamicProgrammingSubSetSumSolver ($$$) { my($numberList, $totalSought, $extractNumber) = @_; my($P, $N) = (0, 0); my $size = scalar(@{$numberList}); my %Q; my(@L) = map { { val => &$extractNumber($_), obj => $_ } } @{$numberList}; print STDERR " TotalSought:", $totalSought if $VERBOSE; print STDERR " L in this iteration:\n [" if $VERBOSE; foreach my $ee (@L) { if ($ee->{val} < 0) { $N += $ee->{val} } else { $P += $ee->{val}; } print STDERR $ee->{val}, ", " if $VERBOSE; } print STDERR "]\n P = $P, N = $N\n" if ($VERBOSE); for (my $ii = 0 ; $ii <= $size ; $ii++ ) { $Q{$ii}{0}{value} = 1; $Q{$ii}{0}{list} = []; } for (my $jj = $N; $jj <= $P ; $jj++) { $Q{0}{$jj}{value} = ($L[0]{val} == $jj); $Q{0}{$jj}{list} = $Q{0}{$jj}{value} ? [ $L[0]{obj} ] : []; } for (my $ii = 1; $ii <= $size ; $ii++ ) { for (my $jj = $N; $jj <= $P ; $jj++) { if ($Q{$ii-1}{$jj}{value}) { $Q{$ii}{$jj}{value} = 1; $Q{$ii}{$jj}{list} = [] unless defined $Q{$ii}{$jj}{list}; push(@{$Q{$ii}{$jj}{list}}, @{$Q{$ii-1}{$jj}{list}}); } elsif ($L[$ii]{val} == $jj) { $Q{$ii}{$jj}{value} = 1; $Q{$ii}{$jj}{list} = [] unless defined $Q{$ii}{$jj}{list}; push(@{$Q{$ii}{$jj}{list}}, $jj); } elsif ($Q{$ii-1}{$jj - $L[$ii]{val}}{value}) { $Q{$ii}{$jj}{value} = 1; $Q{$ii}{$jj}{list} = [] unless defined $Q{$ii}{$jj}{list}; push(@{$Q{$ii}{$jj}{list}}, $L[$ii]{obj}, @{$Q{$ii-1}{$jj - $L[$ii]{val}}{list}}); } else { $Q{$ii}{$jj}{value} = 0; $Q{$ii}{$jj}{list} = []; } } } foreach (my $ii = 0; $ii <= $size; $ii++) { foreach (my $jj = $N; $jj <= $P; $jj++) { print "Q($ii, $jj) == $Q{$ii}{$jj}{value} with List of ", join(", ", @{$Q{$ii}{$jj}{list}}), "\n"; } } return [ $Q{$size}{$totalSought}{value}, \@{$Q{$size}{$totalSought}{list}}]; } ###################################################################### sub Commify ($) { my $text = reverse $_[0]; $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; return scalar reverse $text; } ###################################################################### sub ParseNumber($) { $_[0] =~ s/,//g; return Math::BigFloat->new($_[0]); } ###################################################################### sub ConvertTwoDigitPrecisionToInteger ($) { return sprintf("%d", $_[0] * $ONE_HUNDRED); } ###################################################################### sub ConvertTwoDigitPrecisionToIntegerInEntry ($) { return ConvertTwoDigitPrecisionToInteger($_[0]->{amount}); } ###################################################################### my $firstArg = shift @ARGV; my $solver = \&BruteForceSubSetSumSolver; if (@ARGV < 7) { print STDERR "usage: $0 [-d] <ACCOUNT_REGEX> <END_DATE> <START_SEARCH_FROM_DATE> <END_SEARCH_TO_DATE> <BANK_STATEMENT_BALANCE> <LEDGER_OPTIONS>\n"; exit 1; } if ($firstArg eq '-d') { $solver = \&DynamicProgrammingSubSetSumSolver; } else { unshift(@ARGV, $firstArg); } my($title, $account, $endDate, $startSearchFromDate, $endSearchToDate, $bankBalance, @mainLedgerOptions) = @ARGV; $bankBalance = ParseNumber($bankBalance); my(@fullCommand) = ($LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', '-e', $endDate, '-F', '%t\n', 'bal', "/$account/"); open(FILE, "-|", @fullCommand) or die "unable to run command ledger command: @fullCommand: $!"; my $total; foreach my $line (<FILE>) { chomp $line; die "Unable to parse output line from: \"$line\"" unless $line =~ /^\s*\$\s*([\-\d\.\,]+)\s*$/ and not defined $total; $total = $1; $total = ParseNumber($total); } close FILE; if (not defined $total or $? != 0) { die "unable to run ledger @fullCommand: $!"; } my $differenceSought = $total - $bankBalance; my $err; my $formattedEndDate = UnixDate(DateCalc(ParseDate($endDate), ParseDateDelta("- 1 day"), \$err), "%Y-%m-%d"); die "Date calculation error on $endDate" if ($err); my $earliestStartDate = DateCalc(ParseDate($endDate), ParseDateDelta("- 1 month"), \$err); die "Date calculation error on $endDate" if ($err); my $startDate = ParseDate($startSearchFromDate); my @solution; while ($startDate ge $earliestStartDate) { $startDate = DateCalc(ParseDate($startDate), ParseDateDelta("- 1 day"), \$err); die "Date calculation error on $endDate" if ($err); my $formattedStartDate = UnixDate($startDate, "%Y-%m-%d"); print STDERR "Testing $formattedStartDate through $endSearchToDate for a total of ", Commify($differenceSought), ": \n" if $VERBOSE; my(@fullCommand) = ($LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', '-b', $formattedStartDate, '-e', $endSearchToDate, '-F', '"%(date)","%C","%P","%t"\n', 'reg', "/$account/"); open(FILE, "-|", @fullCommand) or die "unable to run command ledger command: @fullCommand: $!"; my @entries; foreach my $line (<FILE>) { die "Unable to parse output line from: $line" unless $line =~ /^\s*"([^"]*)","([^"]*)","([^"]*)","([^"]*)"\s*$/; my($date, $checkNum, $payee, $amount) = ($1, $2, $3, $4); die "$amount is not a valid amount" unless $amount =~ s/\s*\$\s*([\-\d\.\,]+)\s*$/$1/; $amount = ParseNumber($amount); push(@entries, { date => $date, checkNum => $checkNum, payee => $payee, amount => $amount }); } close FILE; die "unable to properly run ledger command: @fullCommand: $!" unless ($? == 0); @solution = $solver->(\@entries, ConvertTwoDigitPrecisionToInteger($differenceSought), \&ConvertTwoDigitPrecisionToIntegerInEntry); if ($VERBOSE) { if ($solution[0]) { use Data::Dumper; print STDERR "Solution for $formattedStartDate to $formattedEndDate, $differenceSought: \n", Data::Dumper->Dump(\@solution); } else { print STDERR "No Solution Found. :(\n"; } } last if ($solution[0]); } if ($solution[0]) { print "\"title:$formattedEndDate: $title\"\n\"BANK RECONCILIATION: $account\",\"ENDING\",\"$formattedEndDate\"\n"; print "\n\n\"DATE\",\"CHECK NUM\",\"PAYEE\",\"AMOUNT\"\n\n"; print "\"$formattedEndDate\",\"\",\"BANK ACCOUNT BALANCE\",\"\$$bankBalance\"\n\n"; foreach my $ee (sort { $a->{date} cmp $b->{date} } @{$solution[1]}) { print "\"$ee->{date}\",\"$ee->{checkNum}\",\"$ee->{payee}\",\"\$$ee->{amount}\"\n"; } print "\n\"$formattedEndDate\",\"\",\"OUR ACCOUNT BALANCE\",\"\$$total\"\n\n"; } ############################################################################### # # Local variables: # compile-command: "perl -c bank-reconcilation.plx" # End: ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/cash-receipts-and-disbursments-journals.plx�����������0000775�0000000�0000000�00000017543�14411236400�0031716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl # cash-receipts-and-disbursments-journals -*- Perl -*- # # Script to generate a cash receipts and disbursement joural reports # using Ledger. # # Accountants sometimes ask for a report called the "cash receipts and # disbursements journals". From a programmer's perspective, these are two # reports that have the following properties: # # * Receipts: "a list of all transactions in the period where funds # enter a cash account (i.e., the amount reconciled # against the cash account is > 0" # # * Disbursements: "a list of all transactions in the period where # funds leave a cash account (i.e., the amount # reconciled against the cash account is < 0) # # Copyright (C) 2011, 2012, 2013 Bradley M. Kuhn # # This program gives you software freedom; you can copy, modify, convey, # and/or redistribute it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program in a file called 'GPLv3'. If not, write to the: # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor # Boston, MA 02110-1301, USA. use strict; use warnings; use Math::BigFloat; use Date::Manip; use File::Temp qw/tempfile/; my $LEDGER_CMD = "/usr/local/bin/ledger"; my $ACCT_WIDTH = 75; sub ParseNumber($) { $_[0] =~ s/,//g; return Math::BigFloat->new($_[0]); } sub LedgerAcctToFilename($) { my $x = $_[0]; $x =~ s/ /-/g; $x =~ s/:/-/g; return $x; } Math::BigFloat->precision(-2); my $ZERO = Math::BigFloat->new("0.00"); if (@ARGV < 2) { print STDERR "usage: $0 <BEGIN_DATE> <END_DATE> <OTHER_LEDGER_OPTS>\n"; exit 1; } my($beginDate, $endDate, @otherLedgerOpts) = @ARGV; my(@chartOfAccountsOpts) = ('-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'accounts'); open(CHART_DATA, "-|", $LEDGER_CMD, @chartOfAccountsOpts) or die "Unable to run $LEDGER_CMD @chartOfAccountsOpts: $!"; my @accounts; while (my $line = <CHART_DATA>) { chomp $line; next if $line =~ /^\s*\<\s*Adjustment\s*\>\s*$/; next if $line =~ /^Equity:/; # Stupid auto-account made by ledger. $line =~ s/^\s*//; $line =~ s/\s*$//; push(@accounts, $line); } close(CHART_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; my $formattedEndDate = new Date::Manip::Date; die "badly formatted end date, $endDate" if $formattedEndDate->parse($endDate); my $oneDayLess = new Date::Manip::Delta; die "bad one day less" if $oneDayLess->parse("- 1 day"); $formattedEndDate = $formattedEndDate->calc($oneDayLess); $formattedEndDate = $formattedEndDate->printf("%Y/%m/%d"); foreach my $typeData ({ name => 'disbursements', query => 'a<=0' }, { name => 'receipts', query => 'a>0' }) { my $fileNameBase = $typeData->{name}; open(CSV_OUT, ">", "$fileNameBase.csv") or die "unable to open $fileNameBase.csv: $!"; foreach my $acct (sort { $a cmp $b } @accounts) { next unless ($acct =~ /^(?:Assets|Liabilities)/); my @entryLedgerOpts = ('-l', $typeData->{query}, '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'print', $acct); open(ENTRY_DATA, "-|", $LEDGER_CMD, @entryLedgerOpts) or die "Unable to run $LEDGER_CMD @entryLedgerOpts: $!"; my($tempFH, $tempFile) = tempfile("cashreportsXXXXXXXX", TMPDIR => 1); while (my $line = <ENTRY_DATA>) { print $tempFH $line; } close(ENTRY_DATA); die "Error reading ledger output for entries: $!" unless $? == 0; $tempFH->close() or die "Error writing ledger output for entries to temp file, $tempFile: $!"; goto SKIP_REGISTER_COMMANDS if (-z $tempFile); print CSV_OUT "\"ACCOUNT:\",\"$acct\"\n\"PERIOD START:\",\"$beginDate\"\n\"PERIOD END:\",\"$formattedEndDate\"\n"; print CSV_OUT '"DATE","CHECK NUM","NAME","ACCOUNT","AMOUNT"'; my $formatString = '\n"%(date)","%C","%P","%A","%t"'; my $tagStrings = ""; foreach my $tagField (qw/Receipt Invoice Statement Contract PurchaseOrder Approval Check IncomeDistributionAnalysis CurrencyRate/) { print CSV_OUT ',"', $tagField, '"'; $tagStrings .= ',"link:%(tag(\'' . $tagField . '\'))"'; } $formatString .= $tagStrings . '\n%/"","","","%A","%t"' . $tagStrings . '\n'; # I thought '--sort', 'd', '--sort-xact', 'a', should # have worked below for a good sort. Then I tried # rather than '--sort', "d,n,a", which didn't work either. # I opened a bug: https://github.com/ledger/ledger/issues/901 my @csvRegLedgerOpts = ('-f', $tempFile, '-V', '-F', $formatString, '-w', '--sort', 'd', '-b', $beginDate, '-e', $endDate, 'reg'); open(CSV_DATA, "-|", $LEDGER_CMD, @csvRegLedgerOpts) or die "unable to run ledger command for $fileNameBase.csv: $!"; my($curDepositDate, $curDepositTotal); while (my $line = <CSV_DATA>) { $line =~ s/"link:"/""/g; # Skip lines that have Adjustment or Equity: in them. next if $line =~ /^\s*"[^"]*","[^"]*","[^"]*","(\s*\<\s*Adjustment\s*\>\s*|Equity:)/; # Note that we don't do our usual "$TWO_CENTS" check on Adjustment # here. That's by design: if we consistently ignore Adjustments in # the same way, it might have the appearance that a Superman # III/Office Space -style movement of funds is going on. By just # straight "ignoring" them here, and not doing the TWO_CENTS test, it # helps to assure that. # However, it's worth noting that the ignoring of "Adjustment" in these # scripts is not that meaningful and doesn't indicate as Superman # III/Office Space -style scheme, because such a scheme would also have # to be implemented in the main Ledger codebase. my $date = $line; chomp $date; $date =~ s/^\s*"([^"]*)"\s*,.*$/$1/; if (defined $date and $date !~ /^\s*$/ and defined $curDepositDate and ($date ne $curDepositDate or ($date eq $curDepositDate and $line !~ /DEPOSIT[\s\-]+BRANCH/))) { print CSV_OUT "\"$curDepositDate\",\"SUBTOTAL\",\"BRANCH DEPOSIT TOTAL:\",\"\",\"\$$curDepositTotal\"\n\n"; $curDepositTotal = $curDepositDate = undef; } if ($line =~ /DEPOSIT[\s\-]+BRANCH/) { if (not defined $curDepositDate) { $curDepositDate = $line; chomp $curDepositDate; $curDepositDate =~ s/^\s*"([^"]+)"\s*,.*$/$1/; } } # This is a bit of a hack because I can't ssume that the line with the # description on it has the account name in it. if (defined $curDepositDate and $line =~ /$acct/) { my $amt = $line; chomp $amt; $amt =~ s/^\s*"[^"]*","[^"]*","[^"]*","[^"]*","\$\s*([^"]*)".*$/$1/; $amt =~ s/,//g; $curDepositTotal = 0.0 unless defined $curDepositTotal; $curDepositTotal += $amt; } print CSV_OUT $line; } # Catch potential last Deposit subtotal print CSV_OUT "\n\"$curDepositDate\",\"SUBTOTAL\",\"BRANCH DEPOSIT TOTAL:\",\"\",\"\$$curDepositTotal\"\n\n" if (defined $curDepositDate); close(CSV_DATA); die "Error read from csv ledger command $!" unless $? == 0; print CSV_OUT "pagebreak\n"; SKIP_REGISTER_COMMANDS: unlink($tempFile); } close(CSV_OUT); die "Error read write csv out to $fileNameBase.csv: $!" unless $? == 0; } ############################################################################### # # Local variables: # compile-command: "perl -c cash-receipts-and-disbursments-journals.plx" # End: �������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/csv2ods.py��������������������������������������������0000775�0000000�0000000�00000023703�14411236400�0023254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # csv2ods.py # Convert example csv file to ods # # Copyright (c) 2012 Tom Marble # Copyright (c) 2012, 2013 Bradley M. Kuhn # # This program gives you software freedom; you can copy, modify, convey, # and/or redistribute it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program in a file called 'GPLv3'. If not, write to the: # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor # Boston, MA 02110-1301, USA. import sys, os, os.path, optparse import csv import ooolib2 import shutil import string from Crypto.Hash import SHA256 def err(msg): print 'error: %s' % msg sys.exit(1) def ReadChecksums(inputFile): checksums = {} with open(inputFile, "r") as inputFH: entries = inputFH.readlines() for ee in entries: fileName, checksum = ee.split(":") fileName = fileName.replace(' ', "") checksum = checksum.replace(' ', "") checksum = checksum.replace("\n", "") checksums[checksum] = fileName return checksums def ChecksumFile(filename): sha256 = SHA256.new() chunk_size = 8192 with open(filename, 'rb') as myFile: while True: chunk = myFile.read(chunk_size) if len(chunk) == 0: break sha256.update(chunk) return sha256.hexdigest() def main(): program = os.path.basename(sys.argv[0]) print get_file_checksum(sys.argv[1]) def csv2ods(csvname, odsname, encoding='', singleFileDirectory=None, knownChecksums={}, verbose = False): filesSavedinManifest = {} if knownChecksums: checksumCache = {} if verbose: print 'converting from %s to %s' % (csvname, odsname) if singleFileDirectory: if not os.path.isdir(os.path.join(os.getcwd(),singleFileDirectory)): os.mkdir(singleFileDirectory) doc = ooolib2.Calc() # add a pagebreak style style = 'pagebreak' style_pagebreak = doc.styles.get_next_style('row') style_data = tuple([style, ('style:row-height', doc.styles.property_row_height)]) doc.styles.style_config[style_data] = style_pagebreak # add a currency style style = 'currency' style_currency = doc.styles.get_next_style('cell') style_data = tuple([style]) doc.styles.style_config[style_data] = style_currency row = 1 csvdir = os.path.dirname(csvname) if len(csvdir) == 0: csvdir = '.' csvfile = open(csvname, 'rb') reader = csv.reader(csvfile, delimiter=',', quotechar='"') for fields in reader: if len(fields) > 0: for col in range(len(fields)): val = fields[col] if encoding != '' and val[0:5] != "link:": # Only utf8 encode if it's not a filename val = unicode(val, 'utf8') if len(val) > 0 and val[0] == '$': doc.set_cell_value(col + 1, row, 'currency', val[1:]) else: if (len(val) > 0 and val[0:5] == "link:"): val = val[5:] linkname = os.path.basename(val) # name is just the last component newFile = None if not singleFileDirectory: newFile = val if knownChecksums: if not checksumCache.has_key(val): checksum = ChecksumFile(val) checksumCache[val] = checksum else: checksum = checksumCache[val] if knownChecksums.has_key(checksum): newFile = knownChecksums[checksum] print "FOUND new file in known: " + newFile if not newFile: relativeFileWithPath = os.path.basename(val) fileName, fileExtension = os.path.splitext(relativeFileWithPath) newFile = fileName[:15] # 15 is an arbitrary choice. newFile = newFile + fileExtension # We'll now test to see if we made this file # before, and if it matched the same file we # now want. If it doesn't, try to make a # short file name for it. if filesSavedinManifest.has_key(newFile) and filesSavedinManifest[newFile] != val: testFile = None for cc in list(string.letters) + list(string.digits): testFile = cc + newFile if not filesSavedinManifest.has_key(testFile): break testFile = None if not testFile: raise Exception("too many similar file names for linkage; giving up") else: newFile = testFile if not os.path.exists(csvdir + '/' + val): raise Exception("File" + csvdir + '/' + val + " does not exist in single file directory mode; giving up") src = os.path.join(csvdir, val) dest = os.path.join(csvdir, singleFileDirectory, newFile) shutil.copyfile(src, dest) shutil.copystat(src, dest) shutil.copymode(src, dest) newFile = os.path.join(singleFileDirectory, newFile) if knownChecksums: checksumCache[checksum] = newFile knownChecksums[checksum] = newFile linkrel = '../' + newFile # ../ means remove the name of the *.ods doc.set_cell_value(col + 1, row, 'link', (linkrel, linkname)) linkpath = csvdir + '/' + val if not val in filesSavedinManifest: filesSavedinManifest[newFile] = val if not os.path.exists(linkpath): print "WARNING: link %s DOES NOT EXIST at %s" % (val, linkpath) if verbose: if os.path.exists(linkpath): print 'relative link %s EXISTS at %s' % (val, linkpath) else: if val == "pagebreak": doc.sheets[doc.sheet_index].set_sheet_config(('row', row), style_pagebreak) else: if val[0:6] == "title:": doc.sheets[doc.sheet_index].set_name(val[6:]) else: doc.set_cell_value(col + 1, row, 'string', val) else: # enter an empty string for blank lines doc.set_cell_value(1, row, 'string', '') row += 1 # save manifest file if filesSavedinManifest.keys() != []: manifestFH = open("MANIFEST", "a") manifestFH.write("# Files from %s\n" % odsname) for file in filesSavedinManifest.keys(): manifestFH.write("%s\n" % file) manifestFH.close() # Save spreadsheet file. doc.save(odsname) def main(): program = os.path.basename(sys.argv[0]) version = '0.1' parser = optparse.OptionParser(usage='%prog [--help] [--verbose]', version='%prog ' + version) parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='provide extra information while processing') parser.add_option('-c', '--csv', action='store', help='csv file to process') parser.add_option('-o', '--ods', action='store', help='ods output filename') parser.add_option('-e', '--encoding', action='store', help='unicode character encoding type') parser.add_option('-d', '--single-file-directory', action='store', help='directory name to move all files into') parser.add_option('-s', '--known-checksum-list', action='store', help='directory name to move all files into') (options, args) = parser.parse_args() if len(args) != 0: parser.error("not expecting extra args") if not os.path.exists(options.csv): err('csv does not exist: %s' % options.csv) if not options.ods: (root, ext) = os.path.splitext(options.csv) options.ods = root + '.ods' if options.verbose: print '%s: verbose mode on' % program print 'csv:', options.csv print 'ods:', options.ods print 'ods:', options.encoding if options.known_checksum_list and not options.single_file_directory: err(program + ": --known-checksum-list option is completely useless without --single-file-directory") knownChecksums = {} if options.known_checksum_list: if not os.access(options.known_checksum_list, os.R_OK): err(program + ": unable to read file: " + options.known_checksum_list) knownChecksums = ReadChecksums(options.known_checksum_list) csv2ods(options.csv, options.ods, options.encoding, options.single_file_directory, knownChecksums, options.verbose) if __name__ == '__main__': main() �������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/demo.sh�����������������������������������������������0000775�0000000�0000000�00000001744�14411236400�0022600�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # demo.sh # Demonstrate a non-profit GL export and conversion to ODS program=$(basename $0) dir=$(dirname $0) cd $dir dir=$(pwd -P) export PYTHONPATH=$dir/ooolib2 getcsv=$dir/general-ledger-report.plx csv2ods=$dir/csv2ods.py echo "Demonstrating ledger to ODS export in $dir/tests" cd $dir/tests sampledata=non-profit-test-data.ledger echo " based on the sample data in $sampledata" $getcsv 2011/03/01 2012/03/01 -f $sampledata if [ -e general-ledger.csv ]; then echo "data was exported to: general-ledger.csv" else echo "error creating csv file" exit 1 fi $csv2ods --verbose --csv general-ledger.csv if [ -e general-ledger.ods ]; then echo "csv was converted to: general-ledger.ods" else echo "error creating ods file" exit 1 fi echo general-ledger.ods >> MANIFEST # create a portable zip file with the spreadsheet # and the linked artifacts echo creating portable zipfile... cat MANIFEST | zip -@ ../general-ledger.zip echo " " echo "created general-ledger.zip" ����������������������������ledger-3.3.2/contrib/non-profit-audit-reports/fund-report.plx���������������������������������������0000775�0000000�0000000�00000017705�14411236400�0024316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl # fund-report.plx -*- Perl -*- # # Script to generate a Restricted Fund Report. Usefulness of this # script may be confined to those who track separate funds in their # accounts by having accounts that match this format: # /^(Income|Expenses|Unearned Income|(Accrued:[^:]+:):PROJECTNAME/ # Conservancy does this because we carefully track fund balances for our # fiscal sponsored projects. Those who aren't fiscal sponsors won't find # this report all that useful, I suspect. Note that the name # "Conservancy" is special-cased in a few places, mainly because our # "General" fund is called "Conservancy". # # Copyright (C) 2011, 2012, 2013 Bradley M. Kuhn # # This program gives you software freedom; you can copy, modify, convey, # and/or redistribute it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program in a file called 'GPLv3'. If not, write to the: # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor # Boston, MA 02110-1301, USA. use strict; use warnings; use Math::BigFloat; use Date::Manip; my $LEDGER_CMD = "/usr/local/bin/ledger"; my $ACCT_WIDTH = 70; sub ParseNumber($) { $_[0] =~ s/,//g; return Math::BigFloat->new($_[0]); } Math::BigFloat->precision(-2); my $ZERO = Math::BigFloat->new("0.00"); my $TWO_CENTS = Math::BigFloat->new("0.02"); if (@ARGV < 2) { print STDERR "usage: $0 <START_DATE> <END_DATE> <LEDGER_OPTIONS>\n"; exit 1; } my($startDate, $endDate, @mainLedgerOptions) = @ARGV; my $err; my $formattedEndDate = UnixDate(DateCalc(ParseDate($endDate), ParseDateDelta("- 1 day"), \$err), "%Y/%m/%d"); die "Date calculation error on $endDate" if ($err); my $formattedStartDate = UnixDate(ParseDate($startDate), "%Y/%m/%d"); die "Date calculation error on $startDate" if ($err); # First, get balances for starting and ending for each fund my %funds; foreach my $type ('starting', 'ending') { my(@ledgerOptions) = (@mainLedgerOptions, '-V', '-X', '$', '-F', "%-.70A %22.108t\n", '-s'); if ($type eq 'starting') { push(@ledgerOptions, '-e', $startDate); } else { push(@ledgerOptions,'-e', $endDate); } push(@ledgerOptions, 'reg', '/^(Income|Expenses):([^:]+):/'); open(LEDGER_FUNDS, "-|", $LEDGER_CMD, @ledgerOptions) or die "Unable to run $LEDGER_CMD @ledgerOptions: $!"; while (my $fundLine = <LEDGER_FUNDS>) { die "Unable to parse output line from first funds command: \"$fundLine\"" unless $fundLine =~ /^\s*([^\$]+)\s+\$\s*\s*([\-\d\.\,]+)/; my($account, $amount) = ($1, $2); $amount = ParseNumber($amount); $account =~ s/\s+$//; next if $account =~ /\<Adjustment\>/ and (abs($amount) <= $TWO_CENTS); die "Weird account found, $account with amount of $amount in command: @ledgerOptions\n" unless $account =~ s/^\s*(?:Income|Expenses):([^:]+)://; $account = $1; $account = 'General' if $account eq 'Conservancy'; # FIXME: this is a special case for Consrevancy $funds{$account}{$type} += $amount; } close LEDGER_FUNDS; die "Failure on ledger command @ledgerOptions: $!" unless ($? == 0); } foreach my $fund (keys %funds) { foreach my $type (keys %{$funds{$fund}}) { $funds{$fund}{$type} = $ZERO - $funds{$fund}{$type}; } } my(@ledgerOptions) = (@mainLedgerOptions, '-V', '-X', '$', '-F', "%-.70A %22.108t\n", '-w', '-s', '-b', $startDate, '-e', $endDate, 'reg'); my @possibleTypes = ('Income', 'Expenses', 'Unearned Income', 'Retained Earnings', 'Retained Costs', 'Accrued:Loans Receivable', 'Accrued:Accounts Payable', 'Accrued:Accounts Receivable', 'Accrued:Expenses'); foreach my $type (@possibleTypes) { foreach my $fund (keys %funds) { my $query; $query = ($fund eq 'General') ? "/^${type}:Conservancy/": "/^${type}:$fund/"; open(LEDGER_INCOME, "-|", $LEDGER_CMD, @ledgerOptions, $query) or die "Unable to run $LEDGER_CMD for funds: $!"; $funds{$fund}{$type} = $ZERO; while (my $line = <LEDGER_INCOME>) { die "Unable to parse output line from $type line command: $line" unless $line =~ /^\s*([^\$]+)\s+\$\s*\s*([\-\d\.\,]+)/; my($account, $amount) = ($1, $2); $amount = ParseNumber($amount); $funds{$fund}{$type} += $amount; } close LEDGER_INCOME; die "Failure on ledger command for ${type}:$fund: $!" unless ($? == 0); } } my %tot; ($tot{Start}, $tot{End}) = ($ZERO, $ZERO); my %beforeEndings = ('Income' => 1, 'Expenses' => 1); my %afterEndings; # For other @possibleTypes, build up @fieldsList to just those that are present. foreach my $fund (keys %funds) { foreach my $type (@possibleTypes) { if ($funds{$fund}{$type} != $ZERO) { if ($type =~ /^(Unearned Income|Accrued)/) { $afterEndings{$type} = 1; } else { $beforeEndings{$type} = 1; } } } } my(@beforeEndingFields, @afterEndingFields); foreach my $ii (@possibleTypes) { push(@beforeEndingFields, $ii) if defined $beforeEndings{$ii}; push(@afterEndingFields, $ii) if defined $afterEndings{$ii}; } # Make sure fieldLists present items are zero for those that should be zero. foreach my $fund (keys %funds) { foreach my $type ('starting', @beforeEndingFields, 'ending', @afterEndingFields) { $funds{$fund}{$type} = $ZERO unless defined $funds{$fund}{$type}; } } print '"RESTRICTED AND GENERAL FUND REPORT",', "\"BEGINNING:\",\"$formattedStartDate\",\"ENDING:\",\"$formattedEndDate\"\n\n"; print '"FUND","STARTING BALANCE",'; my @finalPrints; foreach my $type (@beforeEndingFields) { $tot{$type} = $ZERO; my $formattedType = $type; print "\"$formattedType\","; } print '"ENDING BALANCE",""'; foreach my $type (@afterEndingFields) { $tot{$type} = $ZERO; my $formattedType = $type; $formattedType = "Prepaid Expenses" if $formattedType eq 'Accrued:Expenses'; $formattedType =~ s/^Accrued://; print ",\"$formattedType\""; } print "\n\n"; sub printTotal ($$) { my($label, $tot) = @_; print "\"$label\",\"\$$tot->{Start}\","; foreach my $type (@beforeEndingFields) { print "\"\$$tot->{$type}\","; } print "\"\$$tot->{End}\",\"\""; foreach my $type (@afterEndingFields) { print ",\"\$$tot->{$type}\""; } print "\n"; } foreach my $fund (sort { if ($a eq "General") { return 1 } elsif ($b eq "General") { return -1 } else { return $a cmp $b } } keys %funds) { my $sanityTotal = $funds{$fund}{starting}; if ($fund eq 'General') { print "\n"; printTotal("Restricted Subtotal", \%tot); print "\n"; } $tot{Start} += $funds{$fund}{starting}; $tot{End} += $funds{$fund}{ending}; print "\"$fund\",\"\$$funds{$fund}{starting}\","; foreach my $type (@beforeEndingFields) { print "\"\$$funds{$fund}{$type}\","; $tot{$type} += $funds{$fund}{$type}; } print "\"\$$funds{$fund}{ending}\",\"\""; foreach my $type (@afterEndingFields) { print ",\"\$$funds{$fund}{$type}\""; $tot{$type} += $funds{$fund}{$type}; } print "\n"; # Sanity check: if (abs($funds{$fund}{ending} - ($funds{$fund}{starting} - $funds{$fund}{Income} - $funds{$fund}{Expenses})) > $TWO_CENTS) { print "$fund FAILED SANITY CHECK: Ending: $funds{$fund}{ending} \n\n\n"; warn "$fund FAILED SANITY CHECK"; } } print "\n"; printTotal("OVERALL TOTAL", \%tot); ############################################################################### # # Local variables: # compile-command: "perl -c fund-report.plx" # End: �����������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/general-ledger-report.plx�����������������������������0000775�0000000�0000000�00000022127�14411236400�0026231�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl # general-ledger-report.plx -*- Perl -*- # # Script to generate a General Ledger report that accountants like # using Ledger. # # Copyright (C) 2011, 2012, 2013 Bradley M. Kuhn # Copyright (C) 2012 Tom Marble # # This program gives you software freedom; you can copy, modify, convey, # and/or redistribute it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program in a file called 'GPLv3'. If not, write to the: # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor # Boston, MA 02110-1301, USA. use strict; use warnings; use Math::BigFloat; use Date::Manip; my $LEDGER_CMD = "/usr/local/bin/ledger"; my $ACCT_WIDTH = 75; sub ParseNumber($) { $_[0] =~ s/,//g; return Math::BigFloat->new($_[0]); } Math::BigFloat->precision(-2); my $ZERO = Math::BigFloat->new("0.00"); if (@ARGV < 3) { print STDERR "usage: $0 <BEGIN_DATE> <END_DATE> <OTHER_LEDGER_OPTS>\n"; exit 1; } open(MANIFEST, ">", "MANIFEST") or die "Unable to open MANIFEST for writing: $!"; my($beginDate, $endDate, @otherLedgerOpts) = @ARGV; my $formattedEndDate = new Date::Manip::Date; die "badly formatted end date, $endDate" if $formattedEndDate->parse($endDate); my $oneDayLess = new Date::Manip::Delta; die "bad one day less" if $oneDayLess->parse("- 1 day"); $formattedEndDate = $formattedEndDate->calc($oneDayLess); $formattedEndDate = $formattedEndDate->printf("%Y/%m/%d"); my $formattedBeginDate = new Date::Manip::Date; die "badly formatted end date, $beginDate" if $formattedBeginDate->parse($beginDate); $formattedBeginDate = $formattedBeginDate->printf("%Y/%m/%d"); my(@chartOfAccountsOpts) = ('-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'accounts'); open(CHART_DATA, "-|", $LEDGER_CMD, @chartOfAccountsOpts) or die "Unable to run $LEDGER_CMD @chartOfAccountsOpts: $!"; my @accounts; while (my $line = <CHART_DATA>) { chomp $line; next if $line =~ /^\s*\<\s*Adjustment\s*\>\s*$/; next if $line =~ /^\s*Equity:/; # Stupid auto-account made by ledger. $line =~ s/^\s*//; $line =~ s/\s*$//; push(@accounts, $line); } close(CHART_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; open(CHART_OUTPUT, ">", "chart-of-accounts.csv") or die "unable to write chart-of-accounts.csv: $!"; print MANIFEST "chart-of-accounts.csv\n"; print CHART_OUTPUT "\"CHART OF ACCOUNTS\","; print CHART_OUTPUT "\"BEGINNING:\",\"$formattedBeginDate\","; print CHART_OUTPUT "\"ENDING:\",\"$formattedEndDate\"\n"; sub preferredAccountSorting ($$) { if ($_[0] =~ /^Assets/ and $_[1] !~ /^Assets/) { return -1; } elsif ($_[1] =~ /^Assets/ and $_[0] !~ /^Assets/) { return 1; } elsif ($_[0] =~ /^Liabilities/ and $_[1] !~ /^(Assets|Liabilities)/) { return -1; } elsif ($_[1] =~ /^Liabilities/ and $_[0] !~ /^(Assets|Liabilities)/) { return 1; } elsif ($_[0] =~ /^(Accrued:[^:]+Receivable)/ and $_[1] !~ /^(Assets|Liabilities|Accrued:[^:]+Receivable)/) { return -1; } elsif ($_[1] =~ /^(Accrued:[^:]+Receivable)/ and $_[0] !~ /^(Assets|Liabilities|Accrued:[^:]+Receivable)/) { return 1; } elsif ($_[0] =~ /^(Accrued)/ and $_[1] !~ /^(Assets|Liabilities|Accrued)/) { return -1; } elsif ($_[1] =~ /^(Accrued)/ and $_[0] !~ /^(Assets|Liabilities|Accrued)/) { return 1; } elsif ($_[0] =~ /^(Unearned Income)/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { return -1; } elsif ($_[1] =~ /^(Unearned Income)/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { return 1; } elsif ($_[0] =~ /^Income/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Unearned Income|Income)/) { return -1; } elsif ($_[1] =~ /^Income/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Unearned Income|Income)/) { return 1; } elsif ($_[0] =~ /^Expense/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Income|Unearned Income|Expense)/) { return -1; } elsif ($_[1] =~ /^Expense/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Income|Unearned Income|Expense)/) { return 1; } else { return $_[0] cmp $_[1]; } } my @sortedAccounts; foreach my $acct ( sort preferredAccountSorting @accounts) { print CHART_OUTPUT "\"$acct\"\n"; push(@sortedAccounts, $acct); } close(CHART_OUTPUT); die "error writing to chart-of-accounts.txt: $!" unless $? == 0; my %commands = ( 'totalEnd' => [ $LEDGER_CMD, @otherLedgerOpts, '-V', '-X', '$', '-e', $endDate, '-F', '%-.80A %22.108t\n', '-s', 'reg' ], 'totalBegin' => [ $LEDGER_CMD, @otherLedgerOpts, '-V', '-X', '$', '-e', $beginDate, '-F', '%-.80A %22.108t\n', '-s', 'reg' ]); my %balanceData; foreach my $id (keys %commands) { my(@command) = @{$commands{$id}}; open(FILE, "-|", @command) or die "unable to run command ledger command: @command: $!"; foreach my $line (<FILE>) { die "Unable to parse output line from balance data $id command: $line" unless $line =~ /^\s*([^\$]+)\s+\$\s*([\-\d\.\,]+)/; my($account, $amount) = ($1, $2); $amount = ParseNumber($amount); $account =~ s/\s+$//; next if $account =~ /\<Adjustment\>/ and (abs($amount) <= 0.02); next if $account =~ /^Equity:/; # Stupid auto-account made by ledger. $balanceData{$id}{$account} = $amount; } close FILE; die "unable to run balance data ledger command, @command: $!" unless ($? == 0); } open(GL_TEXT_OUT, ">", "general-ledger.txt") or die "unable to write general-ledger.txt: $!"; print MANIFEST "general-ledger.txt\n"; open(GL_CSV_OUT, ">", "general-ledger.csv") or die "unable to write general-ledger.csv: $!"; print MANIFEST "general-ledger.csv\n"; my %manifest; foreach my $acct (@sortedAccounts) { print GL_TEXT_OUT "\n\nACCOUNT: $acct\nFROM: $beginDate TO $formattedEndDate\n\n"; my @acctLedgerOpts = ('-V', '-F', "%(date) %-.10C %-.80P %-.80N %18t %18T\n", '-w', '--sort', 'd', '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'reg', '/^' . $acct . '$/'); open(GL_TEXT_DATA, "-|", $LEDGER_CMD, @acctLedgerOpts) or die "Unable to run $LEDGER_CMD @acctLedgerOpts: $!"; foreach my $line (<GL_TEXT_DATA>) { print GL_TEXT_OUT $line; } close(GL_TEXT_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; print GL_CSV_OUT "\n\"ACCOUNT:\",\"$acct\"\n\"PERIOD START:\",\"$formattedBeginDate\"\n\"PERIOD END:\",\"$formattedEndDate\"\n"; print GL_CSV_OUT '"DATE","CHECK NUM","NAME","TRANSACTION AMT","BALANCE"'; my $formatString = '"%(date)","%C","%P","%t",""'; foreach my $tagField (qw/Receipt Invoice Statement Contract PurchaseOrder Approval Check IncomeDistributionAnalysis CurrencyRate/) { print GL_CSV_OUT ',"', $tagField, '"'; $formatString .= ',"link:%(tag(\'' . $tagField . '\'))"'; } $formatString .= "\n"; print GL_CSV_OUT "\n"; if ($acct =~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { $balanceData{totalBegin}{$acct} = $ZERO unless defined $balanceData{totalBegin}{$acct}; print GL_CSV_OUT "\"$formattedBeginDate\"", ',"","BALANCE","","$', "$balanceData{totalBegin}{$acct}\"\n"; } @acctLedgerOpts = ('-V', '-F', $formatString, '-w', '--sort', 'd', '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'reg', '/^' . $acct . '$/'); open(GL_CSV_DATA, "-|", $LEDGER_CMD, @acctLedgerOpts) or die "Unable to run $LEDGER_CMD @acctLedgerOpts: $!"; foreach my $line (<GL_CSV_DATA>) { $line =~ s/"link:"/""/g; print GL_CSV_OUT $line; next if $line =~ /ACCOUNT:.*PERIOD/; # Skip column header lines $line =~ s/^"[^"]*","[^"]*","[^"]*","[^"]*","[^"]*",//; while ($line =~ s/^"([^"]*)"(,|$)//) { my $file = $1; next if $file =~ /^\s*$/; $file =~ s/^link:(.*)$/$1/; warn "$file does not exist and/or is not readable" unless -r $file; print MANIFEST "$file\n" if not defined $manifest{$file}; $manifest{$file} = $line; } } if ($acct =~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { $balanceData{totalEnd}{$acct} = $ZERO unless defined $balanceData{totalEnd}{$acct}; print GL_CSV_OUT "\"$formattedEndDate\"", ',"","BALANCE","","$', "$balanceData{totalEnd}{$acct}\"\n"; } print GL_CSV_OUT "pagebreak\n"; close(GL_CSV_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; } close(GL_TEXT_OUT); die "error writing to general-ledger.txt: $!" unless $? == 0; close(GL_CSV_OUT); die "error writing to general-ledger.csv: $!" unless $? == 0; ############################################################################### # # Local variables: # compile-command: "perl -c general-ledger-report.plx" # End: �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/ooolib2/����������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0022654�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/ooolib2/__init__.py�����������������������������������0000664�0000000�0000000�00000234543�14411236400�0025000�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������"ooolib-python - Copyright (C) 2006-2009 Joseph Colton" # ooolib-python - Python module for creating Open Document Format documents. # Copyright (C) 2006-2009 Joseph Colton # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # You can contact me by email at josephcolton@gmail.com # Import Standard Modules import zipfile # Needed for reading/writing documents import time import sys import glob import os import re import xml.parsers.expat # Needed for parsing documents def version_number(): "Get the ooolib-python version number" return "0.0.17" def version(): "Get the ooolib-python version" return "ooolib-python-%s" % version_number() def clean_string(data): "Returns an XML friendly copy of the data string" data = unicode(data) # This line thanks to Chris Ender data = data.replace('&', '&') data = data.replace("'", ''') data = data.replace('"', '"') data = data.replace('<', '<') data = data.replace('>', '>') data = data.replace('\t', '<text:tab-stop/>') data = data.replace('\n', '<text:line-break/>') return data class XML: "XML Class - Used to convert nested lists into XML" def __init__(self): "Initialize ooolib XML instance" pass def _xmldata(self, data): datatype = data.pop(0) datavalue = data.pop(0) outstring = '%s' % datavalue return outstring def _xmltag(self, data): outstring = '' # First two datatype = data.pop(0) dataname = data.pop(0) outstring = '<%s' % dataname # Element Section element = 1 while(data): # elements newdata = data.pop(0) if (newdata[0] == 'element' and element): newstring = self._xmlelement(newdata) outstring = '%s %s' % (outstring, newstring) continue if (newdata[0] != 'element' and element): element = 0 outstring = '%s>' % outstring if (newdata[0] == 'tag' or newdata[0] == 'tagline'): outstring = '%s\n' % outstring if (newdata[0] == 'tag'): newstring = self._xmltag(newdata) outstring = '%s%s' % (outstring, newstring) continue if (newdata[0] == 'tagline'): newstring = self._xmltagline(newdata) outstring = '%s%s' % (outstring, newstring) continue if (newdata[0] == 'data'): newstring = self._xmldata(newdata) outstring = '%s%s' % (outstring, newstring) continue if (element): element = 0 outstring = '%s>\n' % outstring outstring = '%s</%s>\n' % (outstring, dataname) return outstring def _xmltagline(self, data): outstring = '' # First two datatype = data.pop(0) dataname = data.pop(0) outstring = '<%s' % dataname # Element Section while(data): # elements newdata = data.pop(0) if (newdata[0] != 'element'): break newstring = self._xmlelement(newdata) outstring = '%s %s' % (outstring, newstring) outstring = '%s/>\n' % outstring # Non-Element Section should not exist return outstring def _xmlelement(self, data): datatype = data.pop(0) dataname = data.pop(0) datavalue = data.pop(0) outstring = '%s="%s"' % (dataname, datavalue) return outstring def convert(self, data): """Convert nested lists into XML The convert method takes a nested lists and converts them into XML to be used in Open Document Format documents. There are three types of lists that are recognized at this time. They are as follows: 'tag' - Tag opens a set of data that is eventually closed with a similar tag. List: ['tag', 'xml'] XML: <xml></xml> 'tagline' - Taglines are similar to tags, except they open and close themselves. List: ['tagline', 'xml'] XML: <xml/> 'element' - Elements are pieces of information stored in an opening tag or tagline. List: ['element', 'color', 'blue'] XML: color="blue" 'data' - Data is plain text directly inserted into the XML document. List: ['data', 'hello'] XML: hello Bring them all together for something like this. Lists: ['tag', 'xml', ['element', 'a', 'b'], ['tagline', 'xml2'], ['data', 'asdf']] XML: <xml a="b"><xml2/>asdf</xml> """ outlines = [] outlines.append('<?xml version="1.0" encoding="UTF-8"?>') if (type(data) == type([]) and len(data) > 0): if data[0] == 'tag': outlines.append(self._xmltag(data)) return outlines class Meta: "Meta Data Class" def __init__(self, doctype, debug=False): self.doctype = doctype # Set the debug mode self.debug = debug # The generator should always default to the version number self.meta_generator = version() self.meta_title = '' self.meta_subject = '' self.meta_description = '' self.meta_keywords = [] self.meta_creator = 'ooolib-python' self.meta_editor = '' self.meta_user1_name = 'Info 1' self.meta_user2_name = 'Info 2' self.meta_user3_name = 'Info 3' self.meta_user4_name = 'Info 4' self.meta_user1_value = '' self.meta_user2_value = '' self.meta_user3_value = '' self.meta_user4_value = '' self.meta_creation_date = self.meta_time() # Parser data self.parser_element_list = [] self.parser_element = "" self.parser_count = 0 def set_meta(self, metaname, value): """Set meta data in your document. Currently implemented metaname options are as follows: 'creator' - The document author """ if metaname == 'creator': self.meta_creator = value if metaname == 'editor': self.meta_editor = value if metaname == 'title': self.meta_title = value if metaname == 'subject': self.meta_subject = value if metaname == 'description': self.meta_description = value if metaname == 'user1name': self.meta_user1_name = value if metaname == 'user2name': self.meta_user2_name = value if metaname == 'user3name': self.meta_user3_name = value if metaname == 'user4name': self.meta_user4_name = value if metaname == 'user1value': self.meta_user1_value = value if metaname == 'user2value': self.meta_user2_value = value if metaname == 'user3value': self.meta_user3_value = value if metaname == 'user4value': self.meta_user4_value = value if metaname == 'keyword': if value not in self.meta_keywords: self.meta_keywords.append(value) def get_meta_value(self, metaname): "Get meta data value for a given metaname." if metaname == 'creator': return self.meta_creator if metaname == 'editor': return self.meta_editor if metaname == 'title': return self.meta_title if metaname == 'subject': return self.meta_subject if metaname == 'description': return self.meta_description if metaname == 'user1name': return self.meta_user1_name if metaname == 'user2name': return self.meta_user2_name if metaname == 'user3name': return self.meta_user3_name if metaname == 'user4name': return self.meta_user4_name if metaname == 'user1value': return self.meta_user1_value if metaname == 'user2value': return self.meta_user2_value if metaname == 'user3value': return self.meta_user3_value if metaname == 'user4value': return self.meta_user4_value if metaname == 'keyword': return self.meta_keywords def meta_time(self): "Return time string in meta data format" t = time.localtime() stamp = "%04d-%02d-%02dT%02d:%02d:%02d" % (t[0], t[1], t[2], t[3], t[4], t[5]) return stamp def parse_start_element(self, name, attrs): if self.debug: print '* Start element:', name self.parser_element_list.append(name) self.parser_element = self.parser_element_list[-1] # Need the meta name from the user-defined tags if (self.parser_element == "meta:user-defined"): self.parser_count += 1 # Set user-defined name self.set_meta("user%dname" % self.parser_count, attrs['meta:name']) # Debugging statements if self.debug: print " List: ", self.parser_element_list if self.debug: print " Attributes: ", attrs def parse_end_element(self, name): if self.debug: print '* End element:', name if name != self.parser_element: print "Tag Mismatch: '%s' != '%s'" % (name, self.parser_element) self.parser_element_list.pop() # Readjust parser_element_list and parser_element if (self.parser_element_list): self.parser_element = self.parser_element_list[-1] else: self.parser_element = "" def parse_char_data(self, data): if self.debug: print " Character data: ", repr(data) # Collect Meta data fields if (self.parser_element == "dc:title"): self.set_meta("title", data) if (self.parser_element == "dc:description"): self.set_meta("description", data) if (self.parser_element == "dc:subject"): self.set_meta("subject", data) if (self.parser_element == "meta:initial-creator"): self.set_meta("creator", data) # Try to maintain the same creation date if (self.parser_element == "meta:creation-date"): self.meta_creation_date = data # The user defined fields need to be kept track of, parser_count does that if (self.parser_element == "meta:user-defined"): self.set_meta("user%dvalue" % self.parser_count, data) def meta_parse(self, data): "Parse Meta Data from a meta.xml file" # Debugging statements if self.debug: # Sometimes it helps to see the document that was read from print data print "\n\n\n" # Create parser parser = xml.parsers.expat.ParserCreate() # Set up parser callback functions parser.StartElementHandler = self.parse_start_element parser.EndElementHandler = self.parse_end_element parser.CharacterDataHandler = self.parse_char_data # Actually parse the data parser.Parse(data, 1) def get_meta(self): "Generate meta.xml file data" self.meta_date = self.meta_time() self.data = ['tag', 'office:document-meta', ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'], ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'], ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], ['element', 'office:version', '1.0'], ['tag', 'office:meta', ['tag', 'meta:generator', # Was: 'OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-9011' ['data', self.meta_generator]], # Generator is set the ooolib-python version. ['tag', 'dc:title', ['data', self.meta_title]], # This data is the document title ['tag', 'dc:description', ['data', self.meta_description]], # This data is the document description ['tag', 'dc:subject', ['data', self.meta_subject]], # This data is the document subject ['tag', 'meta:initial-creator', ['data', self.meta_creator]], # This data is the document creator ['tag', 'meta:creation-date', ['data', self.meta_creation_date]], # This is the original creation date of the document ['tag', 'dc:creator', ['data', self.meta_editor]], # This data is the document editor ['tag', 'dc:date', ['data', self.meta_date]], # This is the last modified date of the document ['tag', 'dc:language', ['data', 'en-US']], # We will probably always use en-US for language ['tag', 'meta:editing-cycles', ['data', '1']], # Edit cycles will probably always be 1 for generated documents ['tag', 'meta:editing-duration', ['data', 'PT0S']], # Editing duration is modified - creation date ['tag', 'meta:user-defined', ['element', 'meta:name', self.meta_user1_name], ['data', self.meta_user1_value]], ['tag', 'meta:user-defined', ['element', 'meta:name', self.meta_user2_name], ['data', self.meta_user2_value]], ['tag', 'meta:user-defined', ['element', 'meta:name', self.meta_user3_name], ['data', self.meta_user3_value]], ['tag', 'meta:user-defined', ['element', 'meta:name', self.meta_user4_name], ['data', self.meta_user4_value]]]] # ['tagline', 'meta:document-statistic', # ['element', 'meta:table-count', len(self.sheets)], # len(self.sheets) ? # ['element', 'meta:cell-count', '15']]]] # Not sure how to keep track # Generate content.xml XML data xml = XML() self.lines = xml.convert(self.data) self.filedata = '\n'.join(self.lines) # Return generated data return self.filedata class CalcStyles: "Calc Style Management - Used to keep track of created styles." def __init__(self): self.style_config = {} # Style Counters self.style_table = 1 self.style_column = 1 self.style_row = 1 self.style_cell = 1 # Style Properties (Defaults) - To be used later self.property_column_width_default = '0.8925in' # Default Column Width self.property_row_height_default = '0.189in' # Default Row Height # Set Defaults self.property_column_width = '0.8925in' # Default Column Width self.property_row_height = '0.189in' # Default Row Height self.property_cell_bold = False # Bold off be default self.property_cell_italic = False # Italic off be default self.property_cell_underline = False # Underline off be default self.property_cell_fg_color = 'default' # Text Color Default self.property_cell_bg_color = 'default' # Cell Background Default self.property_cell_bg_image = 'none' # Cell Background Default self.property_cell_fontsize = '10' # Cell Font Size Default self.property_cell_valign = 'default' # Vertical Alignment Default self.property_cell_halign = 'default' # Horizantal Alignment Default def get_next_style(self, style): "Returns the next style code for the given style" style_code = "" if style == 'table': style_code = 'ta%d' % self.style_table self.style_table+=1 if style == 'column': style_code = 'co%d' % self.style_column self.style_column+=1 if style == 'row': style_code = 'ro%d' % self.style_row self.style_row+=1 if style == 'cell': style_code = 'ce%d' % self.style_cell self.style_cell+=1 return style_code def set_property(self, style, name, value): "Sets a property which will later be turned into a code" if style == 'table': pass if style == 'column': if name == 'style:column-width': self.property_column_width = value if style == 'row': if name == 'style:row-height': self.property_row_height = value if style == 'cell': if name == 'bold' and type(value) == type(True): self.property_cell_bold = value if name == 'italic' and type(value) == type(True): self.property_cell_italic = value if name == 'underline' and type(value) == type(True): self.property_cell_underline = value if name == 'fontsize': self.property_cell_fontsize = value if name == 'color': self.property_cell_fg_color = 'default' redata = re.search("^(#[\da-fA-F]{6})$", value) if redata: self.property_cell_fg_color = value.lower() if name == 'background': self.property_cell_bg_color = 'default' redata = re.search("^(#[\da-fA-F]{6})$", value) if redata: self.property_cell_bg_color = value.lower() if name == 'backgroundimage': self.property_cell_bg_image = value if name == 'valign': self.property_cell_valign = value if name == 'halign': self.property_cell_halign = value def get_style_code(self, style): style_code = "" if style == 'table': style_code = "ta1" if style == 'column': style_data = tuple([style, ('style:column-width', self.property_column_width)]) if style_data in self.style_config: # Style Exists, return code style_code = self.style_config[style_data] else: # Style does not exist, create code and return it style_code = self.get_next_style(style) self.style_config[style_data] = style_code if style == 'row': style_data = tuple([style, ('style:row-height', self.property_row_height)]) if style_data in self.style_config: # Style Exists, return code style_code = self.style_config[style_data] else: # Style does not exist, create code and return it style_code = self.get_next_style(style) self.style_config[style_data] = style_code if style == 'cell': style_data = [style] # Add additional styles if self.property_cell_bold: style_data.append(('bold', True)) if self.property_cell_italic: style_data.append(('italic', True)) if self.property_cell_underline: style_data.append(('underline', True)) if self.property_cell_fontsize != '10': style_data.append(('fontsize', self.property_cell_fontsize)) if self.property_cell_fg_color != 'default': style_data.append(('color', self.property_cell_fg_color)) if self.property_cell_bg_color != 'default': style_data.append(('background', self.property_cell_bg_color)) if self.property_cell_bg_image != 'none': style_data.append(('backgroundimage', self.property_cell_bg_image)) if self.property_cell_valign != 'default': style_data.append(('valign', self.property_cell_valign)) if self.property_cell_halign != 'default': style_data.append(('halign', self.property_cell_halign)) style_data = tuple(style_data) if style_data in self.style_config: # Style Exists, return code style_code = self.style_config[style_data] else: # Style does not exist, create code and return it style_code = self.get_next_style(style) self.style_config[style_data] = style_code return style_code def get_automatic_styles(self): "Return 'office:automatic-styles' lists" automatic_styles = ['tag', 'office:automatic-styles'] for style_data in self.style_config: style_code = self.style_config[style_data] style_data = list(style_data) style = style_data.pop(0) if style == 'column': style_list = ['tag', 'style:style', ['element', 'style:name', style_code], # Column 'co1' properties ['element', 'style:family', 'table-column']] tagline = ['tagline', 'style:table-column-properties', ['element', 'fo:break-before', 'auto']] # unsure what break before means for set in style_data: name, value = set if name == 'style:column-width': tagline.append(['element', 'style:column-width', value]) style_list.append(tagline) automatic_styles.append(style_list) if style == 'row': style_list = ['tag', 'style:style', ['element', 'style:name', style_code], # Column 'ro1' properties ['element', 'style:family', 'table-row']] tagline = ['tagline', 'style:table-row-properties'] for set in style_data: name, value = set if name == 'style:row-height': tagline.append(['element', 'style:row-height', value]) tagline.append(['element', 'fo:break-before', 'auto']) # tagline.append(['element', 'style:use-optimal-row-height', 'true']) # Overrides settings style_list.append(tagline) automatic_styles.append(style_list) if style == 'pagebreak': style_list = ['tag', 'style:style', ['element', 'style:name', style_code], # Column 'ro1' properties ['element', 'style:family', 'table-row']] tagline = ['tagline', 'style:table-row-properties'] for set in style_data: name, value = set if name == 'style:row-height': tagline.append(['element', 'style:row-height', value]) tagline.append(['element', 'fo:break-before', 'page']) # tagline.append(['element', 'style:use-optimal-row-height', 'true']) # Overrides settings style_list.append(tagline) automatic_styles.append(style_list) if style == 'cell': style_list = ['tag', 'style:style', ['element', 'style:name', style_code], # ce1 style ['element', 'style:family', 'table-cell'], # cell ['element', 'style:parent-style-name', 'Default']] # parent is Default # hack for currency if style_code == 'ce1': style_list.append(['element', 'style:data-style-name', 'N104']) # Cell Properties tagline = ['tag', 'style:table-cell-properties'] tagline_additional = [] for set in style_data: name, value = set if name == 'background': tagline.append(['element', 'fo:background-color', value]) if name == 'backgroundimage': tagline.append(['element', 'fo:background-color', 'transparent']) # Additional tags added later bgimagetag = ['tagline', 'style:background-image'] bgimagetag.append(['element', 'xlink:href', value]) bgimagetag.append(['element', 'xlink:type', 'simple']) bgimagetag.append(['element', 'xlink:actuate', 'onLoad']) tagline_additional.append(bgimagetag) if name == 'valign': if value in ['top', 'bottom', 'middle']: tagline.append(['element', 'style:vertical-align', value]) if name == 'halign': tagline.append(['element', 'style:text-align-source', 'fix']) if value in ['filled']: tagline.append(['element', 'style:repeat-content', 'true']) else: tagline.append(['element', 'style:repeat-content', 'false']) # Add any additional internal tags while tagline_additional: tagadd = tagline_additional.pop(0) tagline.append(tagadd) style_list.append(tagline) # Paragraph Properties tagline = ['tagline', 'style:paragraph-properties'] tagline_valid = False for set in style_data: name, value = set if name == 'halign': tagline_valid = True if value in ['center']: tagline.append(['element', 'fo:text-align', 'center']) if value in ['end', 'right']: tagline.append(['element', 'fo:text-align', 'end']) if value in ['start', 'filled', 'left']: tagline.append(['element', 'fo:text-align', 'start']) if value in ['justify']: tagline.append(['element', 'fo:text-align', 'justify']) # Conditionally add the tagline if tagline_valid: style_list.append(tagline) # Text Properties tagline = ['tagline', 'style:text-properties'] for set in style_data: name, value = set if name == 'bold': tagline.append(['element', 'fo:font-weight', 'bold']) if name == 'italic': tagline.append(['element', 'fo:font-style', 'italic']) if name == 'underline': tagline.append(['element', 'style:text-underline-style', 'solid']) tagline.append(['element', 'style:text-underline-width', 'auto']) tagline.append(['element', 'style:text-underline-color', 'font-color']) if name == 'color': tagline.append(['element', 'fo:color', value]) if name == 'fontsize': tagline.append(['element', 'fo:font-size', '%spt' % value]) style_list.append(tagline) automatic_styles.append(style_list) # Attach ta1 style automatic_styles.append(['tag', 'style:style', ['element', 'style:name', 'ta1'], ['element', 'style:family', 'table'], ['element', 'style:master-page-name', 'Default'], ['tagline', 'style:table-properties', ['element', 'table:display', 'true'], ['element', 'style:writing-mode', 'lr-tb']]]) return automatic_styles class CalcSheet: "Calc Sheet Class - Used to keep track of the data for an individual sheet." def __init__(self, sheetname): "Initialize a sheet" self.sheet_name = sheetname self.sheet_values = {} self.sheet_config = {} self.max_col = 0 self.max_row = 0 def get_sheet_dimensions(self): "Returns the max column and row" return (self.max_col, self.max_row) def clean_formula(self, data): "Returns a formula for use in ODF" # Example Translations # '=SUM(A1:A2)' # datavalue = 'oooc:=SUM([.A1:.A2])' # '=IF((A5>A4);A4;"")' # datavalue = 'oooc:=IF(([.A5]>[.A4]);[.A4];"")' data = str(data) data = clean_string(data) redata = re.search('^=([A-Z]+)(\(.*)$', data) if redata: # funct is the function name. The rest if the string will be the functArgs funct = redata.group(1) functArgs = redata.group(2) # Search for cell lebels and replace them reList = re.findall('([A-Z]+\d+)', functArgs) # sort and keep track so we do not do a cell more than once reList.sort() lastVar = '' while reList: # Replace each cell label curVar = reList.pop() if curVar == lastVar: continue lastVar = curVar functArgs = functArgs.replace(curVar, '[.%s]' % curVar) data = 'oooc:=%s%s' % (funct, functArgs) return data def get_name(self): "Returns the sheet name" return self.sheet_name def set_name(self, sheetname): "Resets the sheet name" self.sheet_name = sheetname def get_sheet_values(self): "Returns the sheet cell values" return self.sheet_values def get_sheet_value(self, col, row): "Get the value contents of a cell" cell = (col, row) if cell in self.sheet_values: return self.sheet_values[cell] else: return None def get_sheet_config(self): "Returns the sheet cell properties" return self.sheet_config def set_sheet_config(self, location, style_code): "Sets Style Code for a given location" self.sheet_config[location] = style_code def set_sheet_value(self, cell, datatype, datavalue): """Sets the value for a specific cell cell must be in the format (col, row) where row and col are int. Example: B5 would be written as (2, 5) datatype must be one of 'string', 'float', 'formula', 'currency' datavalue should be a string """ # Catch invalid data if type(cell) != type(()) or len(cell) != 2: print "Invalid Cell" return (col, row) = cell if type(col) != type(1): print "Invalid Cell" return if type(row) != type(1): print "Invalid Cell" return # Fix String Data if datatype in ['string', 'annotation']: datavalue = clean_string(datavalue) # Fix Link Data. Link's value is a tuple containing (url, description) if (datatype == 'link'): url = clean_string(datavalue[0]) desc = clean_string(datavalue[1]) datavalue = (url, desc) # Fix Formula Data if datatype == 'formula': datavalue = self.clean_formula(datavalue) # Adjust maximum sizes if col > self.max_col: self.max_col = col if row > self.max_row: self.max_row = row datatype = str(datatype) if (datatype not in ['string', 'float', 'currency', 'formula', 'annotation', 'link']): # Set all unknown cell types to string datatype = 'string' datavalue = str(datavalue) # The following lines are taken directly from HPS # self.sheet_values[cell] = (datatype, datavalue) # HPS: Cell content is now a list of tuples instead of a tuple # While storing here, store the cell contents first and the annotation next. While generating the XML reverse this contents = self.sheet_values.get(cell, {'annotation':None,'link':None, 'value':None}) if datatype == 'annotation': contents['annotation'] = (datatype, datavalue) elif datatype == 'link': contents['link'] = (datatype, datavalue) else: contents['value'] = (datatype, datavalue) self.sheet_values[cell] = contents def get_lists(self): "Returns nested lists for XML processing" if (self.max_col == 0 and self.max_row == 0): sheet_lists = ['tag', 'table:table', ['element', 'table:name', self.sheet_name], # Set the Sheet Name ['element', 'table:style-name', 'ta1'], ['element', 'table:print', 'false'], ['tagline', 'table:table-column', ['element', 'table:style-name', 'co1'], ['element', 'table:default-cell-style-name', 'Default']], ['tag', 'table:table-row', ['element', 'table:style-name', 'ro1'], ['tagline', 'table:table-cell']]] else: # Base Information sheet_lists = ['tag', 'table:table', ['element', 'table:name', self.sheet_name], # Set the sheet name ['element', 'table:style-name', 'ta1'], ['element', 'table:print', 'false']] # ['tagline', 'table:table-column', # ['element', 'table:style-name', 'co1'], # ['element', 'table:number-columns-repeated', self.max_col], # max_col? '2' # ['element', 'table:default-cell-style-name', 'Default']], # Need to add column information for col in range(1, self.max_col+1): location = ('col', col) style_code = 'co1' if location in self.sheet_config: style_code = self.sheet_config[location] sheet_lists.append(['tagline', 'table:table-column', ['element', 'table:style-name', style_code], ['element', 'table:default-cell-style-name', 'Default']]) # Need to create each row for row in range(1, self.max_row + 1): location = ('row', row) style_code = 'ro1' if location in self.sheet_config: style_code = self.sheet_config[location] rowlist = ['tag', 'table:table-row', ['element', 'table:style-name', style_code]] for col in range(1, self.max_col + 1): cell = (col, row) style_code = 'ce1' # Default all cells to ce1 if cell in self.sheet_config: style_code = self.sheet_config[cell] # Lookup cell if available if cell in self.sheet_values: # (datatype, datavalue) = self.sheet_values[cell] # Marked for removal collist = ['tag', 'table:table-cell'] if style_code != 'ce1': collist.append(['element', 'table:style-name', style_code]) # Contents, annotations, and links added by HPS contents = self.sheet_values[cell] # cell contents is a dictionary if contents['value']: (datatype, datavalue) = contents['value'] if datatype == 'float': collist.append(['element', 'office:value-type', datatype]) collist.append(['element', 'office:value', datavalue]) if datatype == 'currency': collist.append(['element', 'table:style-name', "ce1"]) collist.append(['element', 'office:value-type', datatype]) collist.append(['element', 'office:currency', 'USD']) collist.append(['element', 'office:value', datavalue]) if datatype == 'string': collist.append(['element', 'office:value-type', datatype]) if datatype == 'formula': collist.append(['element', 'table:formula', datavalue]) collist.append(['element', 'office:value-type', 'float']) collist.append(['element', 'office:value', '0']) datavalue = '0' else: datavalue = None if contents['annotation']: (annotype, annoval) = contents['annotation'] collist.append(['tag', 'office:annotation', ['tag', 'text:p', ['data', annoval]]]) if contents['link']: (linktype, linkval) = contents['link'] if datavalue: collist.append(['tag', 'text:p', ['data', datavalue], ['tag', 'text:a', ['element', 'xlink:href', linkval[0]], ['data', linkval[1]]]]) else: # no value; just fill the link collist.append(['tag', 'text:p', ['tag', 'text:a', ['element', 'xlink:href', linkval[0]], ['data', linkval[1]]]]) else: if datavalue: collist.append(['tag', 'text:p', ['data', datavalue]]) else: collist = ['tagline', 'table:table-cell'] rowlist.append(collist) sheet_lists.append(rowlist) return sheet_lists class Calc: "Calc Class - Used to create OpenDocument Format Calc Spreadsheets." def __init__(self, sheetname=None, opendoc=None, debug=False): "Initialize ooolib Calc instance" # Default to no debugging self.debug = debug if not sheetname: sheetname = "Sheet1" self.sheets = [CalcSheet(sheetname)] # The main sheet will be initially called 'Sheet1' self.sheet_index = 0 # We initially start on the first sheet self.styles = CalcStyles() self.meta = Meta('ods') self.styles.get_style_code('column') # Force generation of default column self.styles.get_style_code('row') # Force generation of default row self.styles.get_style_code('table') # Force generation of default table self.styles.get_style_code('cell') # Force generation of default cell self.manifest_files = [] # List of extra files included self.manifest_index = 1 # Index of added manifest files # Data Parsing self.parser_element_list = [] self.parser_element = "" self.parser_sheet_num = 0 self.parser_sheet_row = 0 self.parser_sheet_column = 0 self.parser_cell_repeats = 0 self.parser_cell_string_pending = False self.parser_cell_string_line = "" # See if we need to read a document if opendoc: # Verify that the document exists if self.debug: print "Opening Document: %s" % opendoc # Okay, now we load the file self.load(opendoc) def debug_level(self, level): """Set debug level: True if you want debugging messages False if you do not. """ self.debug = level def file_mimetype(self, filename): "Determine the filetype from the filename" parts = filename.lower().split('.') ext = parts[-1] if (ext == 'png'): return (ext, "image/png") if (ext == 'gif'): return (ext, "image/gif") return (ext, "image/unknown") def add_file(self, filename): """Prepare a file for loading into ooolib The filename should be the local filesystem name for the file. The file is then prepared to be included in the creation of the final document. The file needs to remain in place so that it is available when the actual document creation happens. """ # mimetype set to (ext, filetype) mimetype = self.file_mimetype(filename) newname = "Pictures/%08d.%s" % (self.manifest_index, mimetype[0]) self.manifest_index += 1 filetype = mimetype[1] self.manifest_files.append((filename, filetype, newname)) return newname def set_meta(self, metaname, value): "Set meta data in your document." self.meta.set_meta(metaname, value) def get_meta_value(self, metaname): "Get meta data value for a given metaname" return self.meta.get_meta_value(metaname) def get_sheet_name(self): "Returns the sheet name" return self.sheets[self.sheet_index].get_name() def get_sheet_dimensions(self): "Returns the sheet dimensions in (cols, rows)" return self.sheets[self.sheet_index].get_sheet_dimensions() def set_column_property(self, column, name, value): "Set Column Properties" if name == 'width': # column number column needs column-width set to value self.styles.set_property('column', 'style:column-width', value) style_code = self.styles.get_style_code('column') self.sheets[self.sheet_index].set_sheet_config(('col', column), style_code) def set_row_property(self, row, name, value): "Set row Properties" if name == 'height': # row number row needs row-height set to value self.styles.set_property('row', 'style:row-height', value) style_code = self.styles.get_style_code('row') self.sheets[self.sheet_index].set_sheet_config(('row', row), style_code) def set_cell_property(self, name, value): """Turn and off cell properties Actual application of properties is handled by setting a value.""" # background images need to be handled a little differently # because they need to also be inserted into the final document if (name == 'backgroundimage'): # Add file and modify value value = self.add_file(value) self.styles.set_property('cell', name, value) def get_sheet_index(self): "Return the current sheet index number" return self.sheet_index def set_sheet_index(self, index): "Set the sheet index" if type(index) == type(1): if index >= 0 and index < len(self.sheets): self.sheet_index = index return self.sheet_index def get_sheet_count(self): "Returns the number of existing sheets" return len(self.sheets) def new_sheet(self, sheetname): "Create a new sheet" self.sheet_index = len(self.sheets) self.sheets.append(CalcSheet(sheetname)) return self.sheet_index def set_cell_value(self, col, row, datatype, value): "Set the value for a given cell" self.sheets[self.sheet_index].set_sheet_value((col, row), datatype, value) style_code = self.styles.get_style_code('cell') self.sheets[self.sheet_index].set_sheet_config((col, row), style_code) def get_cell_value(self, col, row): "Get a cell value tuple (type, value) for a given cell" sheetvalue = self.sheets[self.sheet_index].get_sheet_value(col, row) # We stop here if there is no value for sheetvalue if sheetvalue == None: return sheetvalue # Now check to see if we have a value tuple if 'value' in sheetvalue: return sheetvalue['value'] else: return None def load(self, filename): """Load .ods spreadsheet. The load function loads data from a document into the current cells. """ # Read in the important files # meta.xml data = self._zip_read(filename, "meta.xml") self.meta.meta_parse(data) # content.xml data = self._zip_read(filename, "content.xml") self.content_parse(data) # settings.xml - I do not remember putting anything here # styles.xml - I do not remember putting anything here def parse_content_start_element(self, name, attrs): if self.debug: print '* Start element:', name self.parser_element_list.append(name) self.parser_element = self.parser_element_list[-1] # Keep track of the current sheet number if (self.parser_element == 'table:table'): # Move to starting cell self.parser_sheet_row = 0 self.parser_sheet_column = 0 # Increment the sheet number count self.parser_sheet_num += 1 if (self.parser_sheet_num - 1 != self.sheet_index): # We are not on the first sheet and need to create a new sheet. # We will automatically move to the new sheet sheetname = "Sheet%d" % self.parser_sheet_num if 'table:name' in attrs: sheetname = attrs['table:name'] self.new_sheet(sheetname) else: # We are on the first sheet and will need to overwrite the default name sheetname = "Sheet%d" % self.parser_sheet_num if 'table:name' in attrs: sheetname = attrs['table:name'] self.sheets[self.sheet_index].set_name(sheetname) # Update the row numbers if (self.parser_element == 'table:table-row'): self.parser_sheet_row += 1 self.parser_sheet_column = 0 # Okay, now keep track of the sheet cell data if (self.parser_element == 'table:table-cell'): # By default it will repeat zero times self.parser_cell_repeats = 0 # We must be in a new column self.parser_sheet_column += 1 # Set some default values datatype = "" value = "" # Get values from attrs hash if 'office:value-type' in attrs: datatype = attrs['office:value-type'] if 'office:value' in attrs: value = attrs['office:value'] if 'table:formula' in attrs: datatype = 'formula' value = attrs['table:formula'] if datatype == 'string': datatype = "" self.parser_cell_string_pending = True self.parser_cell_string_line = "" if 'table:number-columns-repeated' in attrs: self.parser_cell_repeats = int(attrs['table:number-columns-repeated']) - 1 # Set the cell value if datatype: # I should do this once per cell repeat above 0 for i in range(0, self.parser_cell_repeats+1): self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row, datatype, value) # There are lots of interesting cases with table:table-cell data. One problem is # reading the number of embedded spaces correctly. This code should help us get # the number of spaces out. if (self.parser_element == 'text:s'): # This means we have a number of spaces count_num = 0 if 'text:c' in attrs: count_alpha = attrs['text:c'] if (count_alpha.isdigit()): count_num = int(count_alpha) # I am not sure what to do if we do not have a string pending if (self.parser_cell_string_pending == True): # Append the currect number of spaces to the end self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, ' '*count_num) if (self.parser_element == 'text:tab-stop'): if (self.parser_cell_string_pending == True): self.parser_cell_string_line = "%s\t" % (self.parser_cell_string_line) if (self.parser_element == 'text:line-break'): if (self.parser_cell_string_pending == True): self.parser_cell_string_line = "%s\n" % (self.parser_cell_string_line) # Debugging statements if self.debug: print " List: ", self.parser_element_list if self.debug: print " Attributes: ", attrs def parse_content_end_element(self, name): if self.debug: print '* End element:', name if name != self.parser_element: print "Tag Mismatch: '%s' != '%s'" % (name, self.parser_element) self.parser_element_list.pop() # If the element was text:p and we are in string mode if (self.parser_element == 'text:p'): if (self.parser_cell_string_pending): self.parser_cell_string_pending = False # Take care of repeated cells if (self.parser_element == 'table:table-cell'): self.parser_sheet_column += self.parser_cell_repeats # Readjust parser_element_list and parser_element if (self.parser_element_list): self.parser_element = self.parser_element_list[-1] else: self.parser_element = "" def parse_content_char_data(self, data): if self.debug: print " Character data: ", repr(data) if (self.parser_element == 'text:p' or self.parser_element == 'text:span'): if (self.parser_cell_string_pending): # Set the string and leave string pending mode # This does feel a little kludgy, but it does the job self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, data) # I should do this once per cell repeat above 0 for i in range(0, self.parser_cell_repeats+1): self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row, 'string', self.parser_cell_string_line) def content_parse(self, data): "Parse Content Data from a content.xml file" # Debugging statements if self.debug: # Sometimes it helps to see the document that was read from print data print "\n\n\n" # Create parser parser = xml.parsers.expat.ParserCreate() # Set up parser callback functions parser.StartElementHandler = self.parse_content_start_element parser.EndElementHandler = self.parse_content_end_element parser.CharacterDataHandler = self.parse_content_char_data # Actually parse the data parser.Parse(data, 1) def save(self, filename): """Save .ods spreadsheet. The save function saves the current cells and settings into a document. """ if self.debug: print "Writing %s" % filename self.savefile = zipfile.ZipFile(filename, "w") if self.debug: print " meta.xml" self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta()) if self.debug: print " mimetype" self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.spreadsheet") if self.debug: print " Configurations2/accelerator/current.xml" self._zip_insert(self.savefile, "Configurations2/accelerator/current.xml", "") if self.debug: print " META-INF/manifest.xml" self._zip_insert(self.savefile, "META-INF/manifest.xml", self._ods_manifest()) if self.debug: print " content.xml" self._zip_insert(self.savefile, "content.xml", self._ods_content()) if self.debug: print " settings.xml" self._zip_insert(self.savefile, "settings.xml", self._ods_settings()) if self.debug: print " styles.xml" self._zip_insert(self.savefile, "styles.xml", self._ods_styles()) # Add additional files if needed for fileset in self.manifest_files: (filename, filetype, newname) = fileset # Read in the file data = self._file_load(filename) if self.debug: print " Inserting '%s' as '%s'" % (filename, newname) self._zip_insert_binary(self.savefile, newname, data) def _file_load(self, filename): "Load a file" file = open(filename, "rb") data = file.read() file.close() return data def _zip_insert_binary(self, file, filename, data): "Insert a binary file into the zip archive" now = time.localtime(time.time())[:6] info = zipfile.ZipInfo(filename) info.date_time = now info.compress_type = zipfile.ZIP_DEFLATED file.writestr(info, data) def _zip_insert(self, file, filename, data): "Insert a file into the zip archive" # zip seems to struggle with non-ascii characters data = data.encode('utf-8') now = time.localtime(time.time())[:6] info = zipfile.ZipInfo(filename) info.date_time = now info.compress_type = zipfile.ZIP_DEFLATED file.writestr(info, data) def _zip_read(self, file, filename): "Get the data from a file in the zip archive by filename" file = zipfile.ZipFile(file, "r") data = file.read(filename) # Need to close the file file.close() return data def _ods_content(self): "Generate ods content.xml data" # This will list all of the sheets in the document self.sheetdata = ['tag', 'office:spreadsheet'] for sheet in self.sheets: if self.debug: sheet_name = sheet.get_name() print " Creating Sheet '%s'" % sheet_name sheet_list = sheet.get_lists() self.sheetdata.append(sheet_list) # Automatic Styles self.automatic_styles = self.styles.get_automatic_styles() self.data = ['tag', 'office:document-content', ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'], ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'], ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'], ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'], ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'], ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'], ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'], ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'], ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'], ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'], ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'], ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'], ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'], ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'], ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'], ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'], ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'], ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'], ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'], ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'], ['element', 'office:version', '1.0'], ['tagline', 'office:scripts'], ['tag', 'office:font-face-decls', ['tagline', 'style:font-face', ['element', 'style:name', 'DejaVu Sans'], ['element', 'svg:font-family', ''DejaVu Sans''], ['element', 'style:font-pitch', 'variable']], ['tagline', 'style:font-face', ['element', 'style:name', 'Nimbus Sans L'], ['element', 'svg:font-family', ''Nimbus Sans L''], ['element', 'style:font-family-generic', 'swiss'], ['element', 'style:font-pitch', 'variable']]], # Automatic Styles self.automatic_styles, ['tag', 'office:body', self.sheetdata]] # Sheets are generated from the CalcSheet class # Generate content.xml XML data xml = XML() self.lines = xml.convert(self.data) self.filedata = '\n'.join(self.lines) # Return generated data return self.filedata def _ods_manifest(self): "Generate ods manifest.xml data" self.data = ['tag', 'manifest:manifest', ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.spreadsheet'], ['element', 'manifest:full-path', '/']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'application/vnd.sun.xml.ui.configuration'], ['element', 'manifest:full-path', 'Configurations2/']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', ''], ['element', 'manifest:full-path', 'Configurations2/accelerator/']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', ''], ['element', 'manifest:full-path', 'Configurations2/accelerator/current.xml']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'text/xml'], ['element', 'manifest:full-path', 'content.xml']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'text/xml'], ['element', 'manifest:full-path', 'styles.xml']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'text/xml'], ['element', 'manifest:full-path', 'meta.xml']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'text/xml'], ['element', 'manifest:full-path', 'settings.xml']]] # Add additional files to manifest list for fileset in self.manifest_files: (filename, filetype, newname) = fileset addfile = ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', filetype], ['element', 'manifest:full-path', newname]] self.data.append(addfile) # Generate content.xml XML data xml = XML() self.lines = xml.convert(self.data) self.filedata = '\n'.join(self.lines) # Return generated data return self.filedata def _ods_settings(self): "Generate ods settings.xml data" self.data = ['tag', 'office:document-settings', ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], ['element', 'xmlns:config', 'urn:oasis:names:tc:opendocument:xmlns:config:1.0'], ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], ['element', 'office:version', '1.0'], ['tag', 'office:settings', ['tag', 'config:config-item-set', ['element', 'config:name', 'ooo:view-settings'], ['tag', 'config:config-item', ['element', 'config:name', 'VisibleAreaTop'], ['element', 'config:type', 'int'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'VisibleAreaLeft'], ['element', 'config:type', 'int'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'VisibleAreaWidth'], ['element', 'config:type', 'int'], ['data', '6774']], ['tag', 'config:config-item', ['element', 'config:name', 'VisibleAreaHeight'], ['element', 'config:type', 'int'], ['data', '2389']], ['tag', 'config:config-item-map-indexed', ['element', 'config:name', 'Views'], ['tag', 'config:config-item-map-entry', ['tag', 'config:config-item', ['element', 'config:name', 'ViewId'], ['element', 'config:type', 'string'], ['data', 'View1']], ['tag', 'config:config-item-map-named', ['element', 'config:name', 'Tables'], ['tag', 'config:config-item-map-entry', ['element', 'config:name', 'Sheet1'], ['tag', 'config:config-item', ['element', 'config:name', 'CursorPositionX'], # Cursor Position A ['element', 'config:type', 'int'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'CursorPositionY'], # Cursor Position 1 ['element', 'config:type', 'int'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'HorizontalSplitMode'], ['element', 'config:type', 'short'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'VerticalSplitMode'], ['element', 'config:type', 'short'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'HorizontalSplitPosition'], ['element', 'config:type', 'int'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'VerticalSplitPosition'], ['element', 'config:type', 'int'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'ActiveSplitRange'], ['element', 'config:type', 'short'], ['data', '2']], ['tag', 'config:config-item', ['element', 'config:name', 'PositionLeft'], ['element', 'config:type', 'int'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'PositionRight'], ['element', 'config:type', 'int'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'PositionTop'], ['element', 'config:type', 'int'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'PositionBottom'], ['element', 'config:type', 'int'], ['data', '0']]]], ['tag', 'config:config-item', ['element', 'config:name', 'ActiveTable'], ['element', 'config:type', 'string'], ['data', 'Sheet1']], ['tag', 'config:config-item', ['element', 'config:name', 'HorizontalScrollbarWidth'], ['element', 'config:type', 'int'], ['data', '270']], ['tag', 'config:config-item', ['element', 'config:name', 'ZoomType'], ['element', 'config:type', 'short'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'ZoomValue'], ['element', 'config:type', 'int'], ['data', '100']], ['tag', 'config:config-item', ['element', 'config:name', 'PageViewZoomValue'], ['element', 'config:type', 'int'], ['data', '60']], ['tag', 'config:config-item', ['element', 'config:name', 'ShowPageBreakPreview'], ['element', 'config:type', 'boolean'], ['data', 'false']], ['tag', 'config:config-item', ['element', 'config:name', 'ShowZeroValues'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'ShowNotes'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'ShowGrid'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'GridColor'], ['element', 'config:type', 'long'], ['data', '12632256']], ['tag', 'config:config-item', ['element', 'config:name', 'ShowPageBreaks'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'HasColumnRowHeaders'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'HasSheetTabs'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'IsOutlineSymbolsSet'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'IsSnapToRaster'], ['element', 'config:type', 'boolean'], ['data', 'false']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterIsVisible'], ['element', 'config:type', 'boolean'], ['data', 'false']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterResolutionX'], ['element', 'config:type', 'int'], ['data', '1270']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterResolutionY'], ['element', 'config:type', 'int'], ['data', '1270']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterSubdivisionX'], ['element', 'config:type', 'int'], ['data', '1']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterSubdivisionY'], ['element', 'config:type', 'int'], ['data', '1']], ['tag', 'config:config-item', ['element', 'config:name', 'IsRasterAxisSynchronized'], ['element', 'config:type', 'boolean'], ['data', 'true']]]]], ['tag', 'config:config-item-set', ['element', 'config:name', 'ooo:configuration-settings'], ['tag', 'config:config-item', ['element', 'config:name', 'ShowZeroValues'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'ShowNotes'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'ShowGrid'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'GridColor'], ['element', 'config:type', 'long'], ['data', '12632256']], ['tag', 'config:config-item', ['element', 'config:name', 'ShowPageBreaks'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'LinkUpdateMode'], ['element', 'config:type', 'short'], ['data', '3']], ['tag', 'config:config-item', ['element', 'config:name', 'HasColumnRowHeaders'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'HasSheetTabs'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'IsOutlineSymbolsSet'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'IsSnapToRaster'], ['element', 'config:type', 'boolean'], ['data', 'false']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterIsVisible'], ['element', 'config:type', 'boolean'], ['data', 'false']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterResolutionX'], ['element', 'config:type', 'int'], ['data', '1270']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterResolutionY'], ['element', 'config:type', 'int'], ['data', '1270']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterSubdivisionX'], ['element', 'config:type', 'int'], ['data', '1']], ['tag', 'config:config-item', ['element', 'config:name', 'RasterSubdivisionY'], ['element', 'config:type', 'int'], ['data', '1']], ['tag', 'config:config-item', ['element', 'config:name', 'IsRasterAxisSynchronized'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'AutoCalculate'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'PrinterName'], ['element', 'config:type', 'string'], ['data', 'Generic Printer']], ['tag', 'config:config-item', ['element', 'config:name', 'PrinterSetup'], ['element', 'config:type', 'base64Binary'], ['data', 'YgH+/0dlbmVyaWMgUHJpbnRlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0dFTlBSVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAMAqAAAAAAA//8FAFZUAAAkbQAASm9iRGF0YSAxCnByaW50ZXI9R2VuZXJpYyBQcmludGVyCm9yaWVudGF0aW9uPVBvcnRyYWl0CmNvcGllcz0xCnNjYWxlPTEwMAptYXJnaW5kYWp1c3RtZW50PTAsMCwwLDAKY29sb3JkZXB0aD0yNApwc2xldmVsPTAKY29sb3JkZXZpY2U9MApQUERDb250ZXhEYXRhClBhZ2VTaXplOkxldHRlcgAA']], ['tag', 'config:config-item', ['element', 'config:name', 'ApplyUserData'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'CharacterCompressionType'], ['element', 'config:type', 'short'], ['data', '0']], ['tag', 'config:config-item', ['element', 'config:name', 'IsKernAsianPunctuation'], ['element', 'config:type', 'boolean'], ['data', 'false']], ['tag', 'config:config-item', ['element', 'config:name', 'SaveVersionOnClose'], ['element', 'config:type', 'boolean'], ['data', 'false']], ['tag', 'config:config-item', ['element', 'config:name', 'UpdateFromTemplate'], ['element', 'config:type', 'boolean'], ['data', 'false']], ['tag', 'config:config-item', ['element', 'config:name', 'AllowPrintJobCancel'], ['element', 'config:type', 'boolean'], ['data', 'true']], ['tag', 'config:config-item', ['element', 'config:name', 'LoadReadonly'], ['element', 'config:type', 'boolean'], ['data', 'false']]]]] # Generate content.xml XML data xml = XML() self.lines = xml.convert(self.data) self.filedata = '\n'.join(self.lines) # Return generated data return self.filedata def _ods_styles(self): "Generate ods styles.xml data" self.data = ['tag', 'office:document-styles', ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'], ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'], ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'], ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'], ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'], ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'], ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'], ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'], ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'], ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'], ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'], ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'], ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'], ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'], ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'], ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'], ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'], ['element', 'office:version', '1.0'], ['tag', 'office:font-face-decls', ['tagline', 'style:font-face', ['element', 'style:name', 'DejaVu Sans'], ['element', 'svg:font-family', ''DejaVu Sans''], ['element', 'style:font-pitch', 'variable']], ['tagline', 'style:font-face', ['element', 'style:name', 'Nimbus Sans L'], ['element', 'svg:font-family', ''Nimbus Sans L''], ['element', 'style:font-family-generic', 'swiss'], ['element', 'style:font-pitch', 'variable']]], ['tag', 'office:styles', ['tag', 'style:default-style', ['element', 'style:family', 'table-cell'], ['tagline', 'style:table-cell-properties', ['element', 'style:decimal-places', '2']], ['tagline', 'style:paragraph-properties', ['element', 'style:tab-stop-distance', '0.5in']], ['tagline', 'style:text-properties', ['element', 'style:font-name', 'Nimbus Sans L'], ['element', 'fo:language', 'en'], ['element', 'fo:country', 'US'], ['element', 'style:font-name-asian', 'DejaVu Sans'], ['element', 'style:language-asian', 'none'], ['element', 'style:country-asian', 'none'], ['element', 'style:font-name-complex', 'DejaVu Sans'], ['element', 'style:language-complex', 'none'], ['element', 'style:country-complex', 'none']]], ['tag', 'number:number-style', ['element', 'style:name', 'N0'], ['tagline', 'number:number', ['element', 'number:min-integer-digits', '1']]], ['tag', 'number:currency-style', ['element', 'style:name', 'N104P0'], ['element', 'style:volatile', 'true'], ['tag', 'number:currency-symbol', ['element', 'number:language', 'en'], ['element', 'number:country', 'US'], ['data', '$']], ['tagline', 'number:number', ['element', 'number:decimal-places', '2'], ['element', 'number:min-integer-digits', '1'], ['element', 'number:grouping', 'true']]], ['tag', 'number:currency-style', ['element', 'style:name', 'N104'], ['tagline', 'style:text-properties', ['element', 'fo:color', '#ff0000']], ['tag', 'number:text', ['data', '-']], ['tag', 'number:currency-symbol', ['element', 'number:language', 'en'], ['element', 'number:country', 'US'], ['data', '$']], ['tagline', 'number:number', ['element', 'number:decimal-places', '2'], ['element', 'number:min-integer-digits', '1'], ['element', 'number:grouping', 'true']], ['tagline', 'style:map', ['element', 'style:condition', 'value()>=0'], ['element', 'style:apply-style-name', 'N104P0']]], ['tagline', 'style:style', ['element', 'style:name', 'Default'], ['element', 'style:family', 'table-cell']], ['tag', 'style:style', ['element', 'style:name', 'Result'], ['element', 'style:family', 'table-cell'], ['element', 'style:parent-style-name', 'Default'], ['tagline', 'style:text-properties', ['element', 'fo:font-style', 'italic'], ['element', 'style:text-underline-style', 'solid'], ['element', 'style:text-underline-width', 'auto'], ['element', 'style:text-underline-color', 'font-color'], ['element', 'fo:font-weight', 'bold']]], ['tagline', 'style:style', ['element', 'style:name', 'Result2'], ['element', 'style:family', 'table-cell'], ['element', 'style:parent-style-name', 'Result'], ['element', 'style:data-style-name', 'N104']], ['tag', 'style:style', ['element', 'style:name', 'Heading'], ['element', 'style:family', 'table-cell'], ['element', 'style:parent-style-name', 'Default'], ['tagline', 'style:table-cell-properties', ['element', 'style:text-align-source', 'fix'], ['element', 'style:repeat-content', 'false']], ['tagline', 'style:paragraph-properties', ['element', 'fo:text-align', 'center']], ['tagline', 'style:text-properties', ['element', 'fo:font-size', '16pt'], ['element', 'fo:font-style', 'italic'], ['element', 'fo:font-weight', 'bold']]], ['tag', 'style:style', ['element', 'style:name', 'Heading1'], ['element', 'style:family', 'table-cell'], ['element', 'style:parent-style-name', 'Heading'], ['tagline', 'style:table-cell-properties', ['element', 'style:rotation-angle', '90']]]], ['tag', 'office:automatic-styles', ['tag', 'style:page-layout', ['element', 'style:name', 'pm1'], ['tagline', 'style:page-layout-properties', ['element', 'style:writing-mode', 'lr-tb']], ['tag', 'style:header-style', ['tagline', 'style:header-footer-properties', ['element', 'fo:min-height', '0.2957in'], ['element', 'fo:margin-left', '0in'], ['element', 'fo:margin-right', '0in'], ['element', 'fo:margin-bottom', '0.0984in']]], ['tag', 'style:footer-style', ['tagline', 'style:header-footer-properties', ['element', 'fo:min-height', '0.2957in'], ['element', 'fo:margin-left', '0in'], ['element', 'fo:margin-right', '0in'], ['element', 'fo:margin-top', '0.0984in']]]], ['tag', 'style:page-layout', ['element', 'style:name', 'pm2'], ['tagline', 'style:page-layout-properties', ['element', 'style:writing-mode', 'lr-tb']], ['tag', 'style:header-style', ['tag', 'style:header-footer-properties', ['element', 'fo:min-height', '0.2957in'], ['element', 'fo:margin-left', '0in'], ['element', 'fo:margin-right', '0in'], ['element', 'fo:margin-bottom', '0.0984in'], ['element', 'fo:border', '0.0346in solid #000000'], ['element', 'fo:padding', '0.0071in'], ['element', 'fo:background-color', '#c0c0c0'], ['tagline', 'style:background-image']]], ['tag', 'style:footer-style', ['tag', 'style:header-footer-properties', ['element', 'fo:min-height', '0.2957in'], ['element', 'fo:margin-left', '0in'], ['element', 'fo:margin-right', '0in'], ['element', 'fo:margin-top', '0.0984in'], ['element', 'fo:border', '0.0346in solid #000000'], ['element', 'fo:padding', '0.0071in'], ['element', 'fo:background-color', '#c0c0c0'], ['tagline', 'style:background-image']]]]], ['tag', 'office:master-styles', ['tag', 'style:master-page', ['element', 'style:name', 'Default'], ['element', 'style:page-layout-name', 'pm1'], ['tag', 'style:header', ['tag', 'text:p', ['data', '<text:sheet-name>???</text:sheet-name>']]], ['tagline', 'style:header-left', ['element', 'style:display', 'false']], ['tag', 'style:footer', ['tag', 'text:p', ['data', 'Page <text:page-number>1</text:page-number>']]], ['tagline', 'style:footer-left', ['element', 'style:display', 'false']]], ['tag', 'style:master-page', ['element', 'style:name', 'Report'], ['element', 'style:page-layout-name', 'pm2'], ['tag', 'style:header', ['tag', 'style:region-left', ['tag', 'text:p', ['data', '<text:sheet-name>???</text:sheet-name> (<text:title>???</text:title>)']]], ['tag', 'style:region-right', ['tag', 'text:p', ['data', '<text:date style:data-style-name="N2" text:date-value="2006-09-29">09/29/2006</text:date>, <text:time>13:02:56</text:time>']]]], ['tagline', 'style:header-left', ['element', 'style:display', 'false']], ['tag', 'style:footer', ['tag', 'text:p', ['data', 'Page <text:page-number>1</text:page-number> / <text:page-count>99</text:page-count>']]], ['tagline', 'style:footer-left', ['element', 'style:display', 'false']]]]] # Generate content.xml XML data xml = XML() self.lines = xml.convert(self.data) self.filedata = '\n'.join(self.lines) # Return generated data return self.filedata class Writer: "Writer Class - Used to create OpenDocument Format Writer Documents." def __init__(self): "Initialize ooolib Writer instance" # Default to no debugging self.debug = False self.meta = Meta('odt') def set_meta(self, metaname, value): "Set meta data in your document." self.meta.set_meta(metaname, value) def save(self, filename): """Save .odt document The save function saves the current .odt document. """ if self.debug: print "Writing %s" % filename self.savefile = zipfile.ZipFile(filename, "w") if self.debug: print " meta.xml" self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta()) if self.debug: print " mimetype" self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.text") if self.debug: print " META-INF/manifest.xml" self._zip_insert(self.savefile, "META-INF/manifest.xml", self._odt_manifest()) if self.debug: print " content.xml" self._zip_insert(self.savefile, "content.xml", self._odt_content()) if self.debug: print " settings.xml" # self._zip_insert(self.savefile, "settings.xml", self._odt_settings()) if self.debug: print " styles.xml" # self._zip_insert(self.savefile, "styles.xml", self._odt_styles()) # We need to close the file now that we are done creating it. self.savefile.close() def _zip_insert(self, file, filename, data): now = time.localtime(time.time())[:6] info = zipfile.ZipInfo(filename) info.date_time = now info.compress_type = zipfile.ZIP_DEFLATED file.writestr(info, data) def _odt_manifest(self): "Generate odt manifest.xml data" self.data = ['tag', 'manifest:manifest', ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.text'], ['element', 'manifest:full-path', '/']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'text/xml'], ['element', 'manifest:full-path', 'content.xml']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'text/xml'], ['element', 'manifest:full-path', 'styles.xml']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'text/xml'], ['element', 'manifest:full-path', 'meta.xml']], ['tagline', 'manifest:file-entry', ['element', 'manifest:media-type', 'text/xml'], ['element', 'manifest:full-path', 'settings.xml']]] # Generate content.xml XML data xml = XML() self.lines = xml.convert(self.data) self.lines.insert(1, '<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">') self.filedata = '\n'.join(self.lines) # Return generated data return self.filedata def _odt_content(self): "Generate odt content.xml data" self.data = ['tag', 'office:document-content', ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'], ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'], ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'], ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'], ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'], ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'], ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'], ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'], ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'], ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'], ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'], ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'], ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'], ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'], ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'], ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'], ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'], ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'], ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'], ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'], ['element', 'office:version', '1.0'], ['tagline', 'office:scripts'], ['tag', 'office:font-face-decls', ['tagline', 'style:font-face', ['element', 'style:name', 'DejaVu Sans'], ['element', 'svg:font-family', ''DejaVu Sans''], ['element', 'style:font-pitch', 'variable']], ['tagline', 'style:font-face', ['element', 'style:name', 'Nimbus Roman No9 L'], ['element', 'svg:font-family', ''Nimbus Roman No9 L''], ['element', 'style:font-family-generic', 'roman'], ['element', 'style:font-pitch', 'variable']], ['tagline', 'style:font-face', ['element', 'style:name', 'Nimbus Sans L'], ['element', 'svg:font-family', ''Nimbus Sans L''], ['element', 'style:font-family-generic', 'swiss'], ['element', 'style:font-pitch', 'variable']]], ['tagline', 'office:automatic-styles'], ['tag', 'office:body', ['tag', 'office:text', ['tagline', 'office:forms', ['element', 'form:automatic-focus', 'false'], ['element', 'form:apply-design-mode', 'false']], ['tag', 'text:sequence-decls', ['tagline', 'text:sequence-decl', ['element', 'text:display-outline-level', '0'], ['element', 'text:name', 'Illustration']], ['tagline', 'text:sequence-decl', ['element', 'text:display-outline-level', '0'], ['element', 'text:name', 'Table']], ['tagline', 'text:sequence-decl', ['element', 'text:display-outline-level', '0'], ['element', 'text:name', 'Text']], ['tagline', 'text:sequence-decl', ['element', 'text:display-outline-level', '0'], ['element', 'text:name', 'Drawing']]], ['tagline', 'text:p', ['element', 'text:style-name', 'Standard']]]]] # Generate content.xml XML data xml = XML() self.lines = xml.convert(self.data) self.filedata = '\n'.join(self.lines) # Return generated data return self.filedata �������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/readcsv.py��������������������������������������������0000775�0000000�0000000�00000002157�14411236400�0023320�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # readcsv.py # CSV reading technical study # # Copyright (c) 2012 Tom Marble # # This program gives you software freedom; you can copy, modify, convey, # and/or redistribute it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program in a file called 'GPLv3'. If not, write to the: # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor # Boston, MA 02110-1301, USA. import csv dialects = csv.list_dialects() for dialect in dialects: print 'dialect %s' % str(dialect) csvfile = open('tests/general-ledger.csv', 'rb') reader = csv.reader(csvfile, delimiter=',', quotechar='"') for row in reader: print row �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/summary-reports.plx�����������������������������������0000775�0000000�0000000�00000047375�14411236400�0025250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl # fund-report.plx -*- Perl -*- # # Script to generate end-of-year summary reports. # # Copyright (C) 2011, 2012, 2013, Bradley M. Kuhn # # This program gives you software freedom; you can copy, modify, convey, # and/or redistribute it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program in a file called 'GPLv3'. If not, write to the: # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor # Boston, MA 02110-1301, USA. use strict; use warnings; use Math::BigFloat; use Date::Manip; my $VERBOSE = 0; my $DEBUG = 0; my $LEDGER_BIN = "/usr/local/bin/ledger"; my $ACCT_WIDTH = 70; sub Commify ($) { my $text = reverse $_[0]; $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; return scalar reverse $text; } sub preferredAccountSorting ($$) { if ($_[0] =~ /^Assets/ and $_[1] !~ /^Assets/) { return -1; } elsif ($_[1] =~ /^Assets/ and $_[0] !~ /^Assets/) { return 1; } elsif ($_[0] =~ /^Liabilities/ and $_[1] !~ /^(Assets|Liabilities)/) { return -1; } elsif ($_[1] =~ /^Liabilities/ and $_[0] !~ /^(Assets|Liabilities)/) { return 1; } elsif ($_[0] =~ /^(Accrued:[^:]+Receivable)/ and $_[1] !~ /^(Assets|Liabilities|Accrued:[^:]+Receivable)/) { return -1; } elsif ($_[1] =~ /^(Accrued:[^:]+Receivable)/ and $_[0] !~ /^(Assets|Liabilities|Accrued:[^:]+Receivable)/) { return 1; } elsif ($_[0] =~ /^(Accrued)/ and $_[1] !~ /^(Assets|Liabilities|Accrued)/) { return -1; } elsif ($_[1] =~ /^(Accrued)/ and $_[0] !~ /^(Assets|Liabilities|Accrued)/) { return 1; } elsif ($_[0] =~ /^(Unearned Income)/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { return -1; } elsif ($_[1] =~ /^(Unearned Income)/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { return 1; } elsif ($_[0] =~ /^Income/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Unearned Income|Income)/) { return -1; } elsif ($_[1] =~ /^Income/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Unearned Income|Income)/) { return 1; } elsif ($_[0] =~ /^Expense/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Income|Unearned Income|Expense)/) { return -1; } elsif ($_[1] =~ /^Expense/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Income|Unearned Income|Expense)/) { return 1; } else { return $_[0] cmp $_[1]; } } sub ParseNumber($) { $_[0] =~ s/,//g; return Math::BigFloat->new($_[0]); } Math::BigFloat->precision(-2); my $ZERO = Math::BigFloat->new("0.00"); my $ONE_PENNY = Math::BigFloat->new("0.01"); my $TWO_CENTS = Math::BigFloat->new("0.02"); if (@ARGV < 2) { print STDERR "usage: $0 <START_DATE> <END_DATE> <LEDGER_OPTIONS>\n"; exit 1; } my($startDate, $endDate, @mainLedgerOptions) = @ARGV; my $err; my $formattedEndDate = UnixDate(DateCalc(ParseDate($endDate), ParseDateDelta("- 1 day"), \$err), "%B %e, %Y"); die "Date calculation error on $endDate" if ($err); my $formattedStartDate = UnixDate(ParseDate($startDate), "%B %e, %Y"); die "Date calculation error on $startDate" if ($err); my %reportFields = ('Cash' => { args => [ '-e', $endDate, 'bal', '/^Assets/' ] }, 'Accounts Receivable' => {args => [ '-e', $endDate, 'bal', '/^Accrued:Accounts Receivable/' ]}, 'Loans/Fraud Receivable' => {args => [ '-e', $endDate, 'bal', '/^Accrued:(Loans|Fraud) Receivable/' ]}, 'Accounts Payable' => {args => [ '-e', $endDate, 'bal', '/^Accrued.*Accounts Payable/' ]}, 'Accrued Expenses' => {args => [ '-e', $endDate, 'bal', '/^Accrued.*Expenses/' ]}, 'Liabilities, Credit Cards' => {args => [ '-e', $endDate, 'bal', '/^Liabilities:Credit Card/' ]}, 'Liabilities, Other' => {args => [ '-e', $endDate, 'bal', '/^Liabilities/', 'and', 'not', '/^Liabilities:Credit Card/']}, 'Unearned Income, Conference Registration' => {args => [ '-e', $endDate, 'bal', '/^Unearned Income.*Reg/' ]}, 'Unearned Income, Other' => {args => [ '-e', $endDate, 'bal', '/^Unearned Income/', 'and', 'not', '/^Unearned Income.*Reg/' ]}, 'Unrestricted Net Assets' => {args => [ '-e', $endDate, 'bal', '/^(Income|Expenses):Conservancy/' ]}, 'Temporarily Restricted Net Assets' => {args => [ '-e', $endDate, 'bal', '/^(Income|Expenses)/', 'and', 'not', '/^(Unearned Income|(Income|Expenses):Conservancy)/' ]}, 'Total Net Assets' => {args => [ '-e', $endDate, 'bal', '/^(Income|Expenses)/' ]}, ); foreach my $item (keys %reportFields) { my(@fullCommand) = ($LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', '-S', 'T', '-s', '-d', 'T', @{$reportFields{$item}{args}}); open(FILE, "-|", @fullCommand) or die "unable to run command ledger command: @fullCommand: $!"; my $foundBalance; my $seenTotalLine = 0; print STDERR ($VERBOSE ? "Running: @fullCommand\n" : "."); print STDERR " Output of @fullCommand\n" if $DEBUG; while (my $line = <FILE>) { print STDERR $line if ($DEBUG); $seenTotalLine = 1 if $line =~ /^\s*\-+\s*/; # Skip lines until the total line $foundBalance = $1 if (not $seenTotalLine and $line =~ /^\s*[^0-9\-]+\s*([\-\d,\.]+)\s+/); if ($line =~ /^\s*\$\s*([\-\d,\.]+)\s*$/) { $foundBalance = $1; last; } } close FILE; die "problem running ledger command: @fullCommand: $!" unless ($? == 0); if (not defined $foundBalance) { $foundBalance = $ZERO; } else { $foundBalance =~ s/,//g; $foundBalance = Math::BigFloat->new($foundBalance); } $foundBalance = $ZERO if not defined $foundBalance; $reportFields{$item}{total} = abs($foundBalance); print STDERR "$item: $reportFields{$item}{total}\n" if $VERBOSE; } open(BALANCE_SHEET, ">", "balance-sheet.csv") or die "unable to open balance-sheet.csv for writing: $!"; print BALANCE_SHEET "\"BALANCE SHEET\"\n", "\"Ending\",\"", $formattedEndDate, "\"\n", "\n\n\"ASSETS\"\n\n"; my $formatStr = "\"\",\"%-42s\",\"\$%13s\"\n"; my $formatStrTotal = "\"\",\"%-45s\",\"\$%13s\"\n"; my $tot = $ZERO; foreach my $item ('Cash', 'Accounts Receivable', 'Loans/Fraud Receivable') { next if $reportFields{$item}{total} == $ZERO; print BALANCE_SHEET sprintf($formatStr, "$item:", Commify($reportFields{$item}{total})); $tot += $reportFields{$item}{total}; } print BALANCE_SHEET "\n", sprintf($formatStrTotal, "TOTAL ASSETS", Commify($tot)), "\n\nLIABILITIES\n\n"; my $totLiabilities = $ZERO; foreach my $item ('Accounts Payable', 'Accrued Expenses', 'Liabilities, Credit Cards', 'Liabilities, Other', 'Unearned Income, Conference Registration', 'Unearned Income, Other') { next if $reportFields{$item}{total} == $ZERO; print BALANCE_SHEET sprintf($formatStr, "$item:", Commify($reportFields{$item}{total})); $totLiabilities += $reportFields{$item}{total}; } print BALANCE_SHEET "\n", sprintf($formatStr, "TOTAL LIABILTIES", Commify($totLiabilities)), "\n\nNET ASSETS\n\n"; my $totNetAssets = $ZERO; foreach my $item ('Unrestricted Net Assets', 'Temporarily Restricted Net Assets') { next if $reportFields{$item}{total} == $ZERO; print BALANCE_SHEET sprintf($formatStr, "$item:", Commify($reportFields{$item}{total})); $totNetAssets += $reportFields{$item}{total}; } print BALANCE_SHEET "\n", sprintf($formatStr, "TOTAL NET ASSETS", Commify($totNetAssets)), "\n\n", sprintf($formatStrTotal, "TOTAL LIABILITIES AND NET ASSETS", Commify($totNetAssets + $totLiabilities)); close BALANCE_SHEET; print STDERR "\n"; die "unable to write to balance-sheet.csv: $!" unless ($? == 0); die "Cash+accounts receivable total does not equal net assets and liabilities total" if (abs( ($reportFields{'Cash'}{total} + $reportFields{'Accounts Receivable'}{total} + $reportFields{'Loans/Fraud Receivable'}{total})) - abs($reportFields{'Accounts Payable'}{total} + $reportFields{'Accrued Expenses'}{total} + $reportFields{'Unearned Income, Conference Registration'}{total} + $reportFields{'Unearned Income, Other'}{total} + $reportFields{'Liabilities, Credit Cards'}{total} + $reportFields{'Liabilities, Other'}{total} + $reportFields{'Total Net Assets'}{total}) > $TWO_CENTS); die "Total net assets doesn't equal sum of restricted and unrestricted ones!" if (abs($reportFields{'Total Net Assets'}{total}) - abs($reportFields{'Unrestricted Net Assets'}{total} + $reportFields{'Temporarily Restricted Net Assets'}{total}) > $TWO_CENTS); my %incomeGroups = ('INTEREST INCOME' => { args => ['/^Income.*Interest/' ] }, 'DONATIONS' => { args => [ '/^Income.*Donation/' ] }, 'BOOK ROYALTIES & AFFILIATE PROGRAMS' => { args => [ '/^Income.*(Royalt|Affiliate)/' ] }, 'CONFERENCES, REGISTRATION' => {args => [ '/^Income.*Reg/' ] }, 'CONFERENCES, RELATED BUSINESS INCOME' => { args => [ '/^Income.*(Conferences?:.*Sponsor|Booth|RBI)/'] }, 'LICENSE COMPLIANCE' => { args => [ '/^Income.*(Enforce|Compliance)/' ]}, 'TRADEMARKS' => {args => [ '/^Income.*Trademark/' ]}, 'ADVERTISING' => {args => [ '/^Income.*Advertising/' ]}); my @otherArgs; foreach my $type (keys %incomeGroups) { @otherArgs = ("/^Income/") if @otherArgs == 0; push(@otherArgs, 'and', 'not', @{$incomeGroups{$type}{args}}); } $incomeGroups{"OTHER"}{args} = \@otherArgs; $incomeGroups{"TOTAL"}{args} = ['/^Income/']; open(INCOME, ">", "income.csv") or die "unable to open income.csv for writing: $!"; foreach my $type (keys %incomeGroups) { my(@fullCommand) = ($LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', '-b', $startDate, '-e', $endDate, '-F', '%-.80A %22.108t\n', '-s', 'reg', @{$incomeGroups{$type}{args}}); open(FILE, "-|", @fullCommand) or die "unable to run command ledger command: @fullCommand: $!"; print STDERR ($VERBOSE ? "Running: @fullCommand\n" : "."); $incomeGroups{$type}{total} = $ZERO; $incomeGroups{$type}{output} = ""; foreach my $line (<FILE>) { die "Unable to parse output line from second funds command: $line" unless $line =~ /^\s*([^\$]+)\s+\$\s*([\-\d\.\,]+)/; my($account, $amount) = ($1, $2); $amount = ParseNumber($amount); $account =~ s/\s+$//; next if $account =~ /\<Adjustment\>/ and (abs($amount) <= $TWO_CENTS); die "Weird account found, $account with amount of $amount in income command\n" unless $account =~ /^\s*Income:/; $incomeGroups{$type}{total} += $amount; $incomeGroups{$type}{output} .= "\"$account\",\"\$$amount\"\n"; } } print INCOME "\"INCOME\",", "\"STARTING:\",\"$formattedStartDate\",\"ENDING:\",\"$formattedEndDate\"\n\n"; my $overallTotal = $ZERO; $formatStrTotal = "\"%-90s\",\"\$%14s\"\n"; foreach my $type ('DONATIONS', 'LICENSE COMPLIANCE', 'CONFERENCES, REGISTRATION', 'CONFERENCES, RELATED BUSINESS INCOME', 'BOOK ROYALTIES & AFFILIATE PROGRAMS', 'ADVERTISING', 'TRADEMARKS', 'INTEREST INCOME', 'OTHER') { next if ($incomeGroups{$type}{output} =~ /^\s*$/ and $incomeGroups{$type}{total} == $ZERO); print INCOME "\n\"$type\"\n", $incomeGroups{$type}{output}, "\n", sprintf($formatStrTotal, "TOTAL $type:", Commify($incomeGroups{$type}{total})); $overallTotal += $incomeGroups{$type}{total}; } print INCOME "\n\n\n", sprintf($formatStrTotal, "OVERALL TOTAL:", Commify($overallTotal)); close INCOME; die "unable to write to income.csv: $!" unless ($? == 0); die "calculated total of $overallTotal does equal $incomeGroups{TOTAL}{total}" if (abs($overallTotal) - abs($incomeGroups{TOTAL}{total}) > $TWO_CENTS); print STDERR "\n"; my %expenseGroups = ('BANKING FEES' => { regex => '^Expenses.*(Banking Fees|Currency Conversion)' }, 'COMPUTING, HOSTING AND EQUIPMENT' => { regex => '^Expenses.*(Hosting|Computer Equipment)' }, 'CONFERENCES' => { regex => '^Expenses.*(Conferences|Sprint)' }, 'DEVELOPER MENTORING' => {regex => '^Expenses.*Mentor' }, 'LICENSE COMPLIANCE' => { regex => '^Expenses.*(Enforce|Compliance)' }, 'ACCOUNTING' => { regex => '^Expenses.*(Accounting|Annual Audit)' }, 'PAYROLL' => { regex => '^Expenses.*Payroll' }, 'OFFICE' => { regex => '^Expenses.*(Office|Phones)' }, 'RENT' => { regex => '^Expenses.*Rent' }, 'SOFTWARE DEVELOPMENT' => { regex => '^Expenses.*Development' }, 'OTHER PROGRAM ACTIVITY' => {regex => '^Expenses.*Gould' }, 'ADVOCACY AND PROMOTION' => {regex => '^Expenses.*(Slipstream|Advocacy Merchandise|Promotional)' }, 'ADVERTISING' => {regex => '^Expenses.*Advertising' }); foreach my $type (keys %expenseGroups, 'TRAVEL') { $expenseGroups{$type}{total} = $ZERO; $expenseGroups{$type}{output} = ""; } open(EXPENSE, ">", "expense.csv") or die "unable to open expense.csv for writing: $!"; my(@fullCommand) = ($LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', '-b', $startDate, '-e', $endDate, '-F', '%-.80A %22.108t\n', '-s', 'reg', '/^Expenses/'); open(FILE, "-|", @fullCommand) or die "unable to run command ledger command: @fullCommand: $!"; print STDERR ($VERBOSE ? "Running: @fullCommand\n" : "."); my $firstTotal = $ZERO; foreach my $line (<FILE>) { die "Unable to parse output line from second funds command: $line" unless $line =~ /^\s*([^\$]+)\s+\$\s*([\-\d\.\,]+)/; my($account, $amount) = ($1, $2); $amount = ParseNumber($amount); $account =~ s/\s+$//; next if $account =~ /\<Adjustment\>/ and (abs($amount) <= $TWO_CENTS); die "Weird account found, $account, with amount of $amount in expenses command\n" unless $account =~ /^\s*Expenses:/; my $outputLine = "\"$account\",\"\$$amount\"\n"; my $taken = 0; # Note: Prioritize to put things under conference expenses if they were for a conference. foreach my $type ('CONFERENCES', keys %expenseGroups) { last if $taken; next if $type eq 'TRAVEL' or $type eq 'OTHER'; next unless $line =~ /$expenseGroups{$type}{regex}/; $taken = 1; $expenseGroups{$type}{total} += $amount; $expenseGroups{$type}{output} .= $outputLine; } if (not $taken) { if ($account =~ /Travel/) { $expenseGroups{'TRAVEL'}{total} += $amount; $expenseGroups{'TRAVEL'}{output} .= $outputLine; } else { $expenseGroups{'OTHER'}{total} += $amount; $expenseGroups{'OTHER'}{output} .= $outputLine; } } $firstTotal += $amount; } print EXPENSE "\"EXPENSES\",", "\"STARTING:\",\"$formattedStartDate\",\"ENDING:\",\"$formattedEndDate\"\n\n"; $overallTotal = $ZERO; $formatStrTotal = "\"%-90s\",\"\$%14s\"\n"; my %verifyAllGroups; foreach my $key (keys %expenseGroups) { $verifyAllGroups{$key} = 1; } foreach my $type ('PAYROLL', 'SOFTWARE DEVELOPMENT', 'LICENSE COMPLIANCE', 'CONFERENCES', 'DEVELOPER MENTORING', 'TRAVEL', 'BANKING FEES', 'ADVOCACY AND PROMOTION', 'COMPUTING, HOSTING AND EQUIPMENT', 'ACCOUNTING', 'OFFICE', 'RENT', 'ADVERTISING', 'OTHER PROGRAM ACTIVITY', 'OTHER') { delete $verifyAllGroups{$type}; die "$type is not defined!" if not defined $expenseGroups{$type}; next if ($expenseGroups{$type}{output} =~ /^\s*$/ and $expenseGroups{$type}{total} == $ZERO); print EXPENSE "\n\"$type\"\n", $expenseGroups{$type}{output}, "\n", sprintf($formatStrTotal, "TOTAL $type:", Commify($expenseGroups{$type}{total})); $overallTotal += $expenseGroups{$type}{total}; } print EXPENSE "\n\n\n", sprintf($formatStrTotal, "OVERALL TOTAL:", Commify($overallTotal)); close EXPENSE; die "unable to write to expense.csv: $!" unless ($? == 0); die "GROUPS NOT INCLUDED : ", join(keys(%verifyAllGroups), ", "), "\n" unless (keys %verifyAllGroups == 0); die "calculated total of $overallTotal does *not* equal $firstTotal" if (abs($overallTotal) - abs($firstTotal) > $TWO_CENTS); print STDERR "\n"; open(TRIAL, ">", "trial-balance.csv") or die "unable to open accrued.txt for writing: $!"; print TRIAL "\"TRIAL BALANCE REPORT\",\"ENDING: $formattedEndDate\"\n\n", "\"ACCOUNT\",\"BALANCE AT $formattedStartDate\",\"CHANGE DURING PERIOD\",\"BALANCE AT $formattedEndDate\"\n\n"; my %commands = ( 'totalEndFY' => [ $LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', '-e', $endDate, '-F', '%-.80A %22.108t\n', '-s', 'reg' ], 'amountInYear' => [ $LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', '-b', $startDate, '-e', $endDate, '-F', '%-.80A %22.108t\n', '-s', 'reg' ], 'totalBeginFY' => [ $LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', '-e', $startDate, '-F', '%-.80A %22.108t\n', '-s', 'reg' ]); my %trialBalanceData; my %fullAccountList; foreach my $id (keys %commands) { my(@command) = @{$commands{$id}}; open(FILE, "-|", @command) or die "unable to run command ledger command: @command: $!"; print STDERR ($VERBOSE ? "Running: @command\n" : "."); foreach my $line (<FILE>) { die "Unable to parse output line from trial balance $id command: $line" unless $line =~ /^\s*([^\$]+)\s+\$\s*([\-\d\.\,]+)/; my($account, $amount) = ($1, $2); $amount = ParseNumber($amount); $account =~ s/\s+$//; next if $account =~ /\<Adjustment\>/ and (abs($amount) <= $TWO_CENTS); next if $account =~ /^Equity:/; # Stupid auto-account made by ledger. $trialBalanceData{$id}{$account} = $amount; $fullAccountList{$account} = $id; } close FILE; die "unable to run trial balance ledger command, @command: $!" unless ($? == 0); } my $curOn = 'Assets'; foreach my $account (sort preferredAccountSorting keys %fullAccountList) { # Blank lines right if ($account !~ /^$curOn/) { print TRIAL "pagebreak\n"; $curOn = $account; if ($curOn =~ /(Accrued:[^:]+):.*$/) { $curOn = $1; } else { $curOn =~ s/^([^:]+):.*$/$1/; } } if ($account =~ /^Assets|Liabilities|Accrued|Unearned Income/) { foreach my $id (qw/totalBeginFY totalEndFY amountInYear/) { $trialBalanceData{$id}{$account} = $ZERO unless defined $trialBalanceData{$id}{$account}; } print TRIAL "\"$account\",\"\$$trialBalanceData{totalBeginFY}{$account}\",", "\"\$$trialBalanceData{amountInYear}{$account}\",\"\$$trialBalanceData{totalEndFY}{$account}\"\n" unless $trialBalanceData{totalBeginFY}{$account} == $ZERO and $trialBalanceData{amountInYear}{$account} == $ZERO and $trialBalanceData{totalEndFY}{$account} == $ZERO; } else { print TRIAL "\"$account\",\"\",\"\$$trialBalanceData{amountInYear}{$account}\",\"\"\n" if defined $trialBalanceData{amountInYear}{$account} and $trialBalanceData{amountInYear}{$account} != $ZERO; } } close TRIAL; die "unable to write trial-balance.csv: $!" unless ($? == 0); ############################################################################### # # Local variables: # compile-command: "perl -c summary-reports.plx" # End: �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0022451�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Financial/��������������������������������������0000775�0000000�0000000�00000000000�14411236400�0024335�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Financial/BankStuff/����������������������������0000775�0000000�0000000�00000000000�14411236400�0026220�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Financial/BankStuff/bank-statement.pdf����������0000664�0000000�0000000�00000006271�14411236400�0031636�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%PDF-1.4 %쏢 5 0 obj <</Length 6 0 R/Filter /FlateDecode>> stream xN0EY J, #1- j({N*ذ@eپ3�#0u]¦ehn؁a+$�m//F X!B yF�Ӣ+:_oL*.?_@±hB_͙ۜLpDG`*fggKf,Ii58@DԘBI1R<ī Bu-TK fN0LMȔ"jP(=gGJ6U*zVh_&Cʁ5i4=k^Η:)ʽ?#mM4(O4{e۲8j,P@͕@|ξendstream endobj 6 0 obj 341 endobj 4 0 obj <</Type/Page/MediaBox [0 0 612 792] /Rotate 0/Parent 3 0 R /Resources<</ProcSet[/PDF /Text] /ExtGState 11 0 R /Font 12 0 R >> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <</Type /Catalog /Pages 3 0 R /Metadata 14 0 R >> endobj 7 0 obj <</Type/ExtGState /OPM 1>>endobj 11 0 obj <</R7 7 0 R>> endobj 12 0 obj <</R9 9 0 R/R8 8 0 R/R10 10 0 R>> endobj 9 0 obj <</BaseFont/Helvetica/Type/Font /Subtype/Type1>> endobj 8 0 obj <</BaseFont/Courier/Type/Font /Subtype/Type1>> endobj 10 0 obj <</BaseFont/Helvetica-Bold/Type/Font /Encoding 13 0 R/Subtype/Type1>> endobj 13 0 obj <</Type/Encoding/Differences[ 45/minus]>> endobj 14 0 obj <</Type/Metadata /Subtype/XML/Length 1404>>stream <?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> <?adobe-xap-filters esc="CRLF"?> <x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'> <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:iX='http://ns.adobe.com/iX/1.0/'> <rdf:Description rdf:about='67bdd46a-6c22-11ed-0000-635a4c31e8cd' xmlns:pdf='http://ns.adobe.com/pdf/1.3/' pdf:Producer='GPL Ghostscript 8.71'/> <rdf:Description rdf:about='67bdd46a-6c22-11ed-0000-635a4c31e8cd' xmlns:xmp='http://ns.adobe.com/xap/1.0/'><xmp:ModifyDate>2012-11-21T13:04:13-05:00</xmp:ModifyDate> <xmp:CreateDate>2012-11-21T13:04:13-05:00</xmp:CreateDate> <xmp:CreatorTool>a2ps version 4.14</xmp:CreatorTool></rdf:Description> <rdf:Description rdf:about='67bdd46a-6c22-11ed-0000-635a4c31e8cd' xmlns:xapMM='http://ns.adobe.com/xap/1.0/mm/' xapMM:DocumentID='67bdd46a-6c22-11ed-0000-635a4c31e8cd'/> <rdf:Description rdf:about='67bdd46a-6c22-11ed-0000-635a4c31e8cd' xmlns:dc='http://purl.org/dc/elements/1.1/' dc:format='application/pdf'><dc:title><rdf:Alt><rdf:li xml:lang='x-default'>bank-statement.txt</rdf:li></rdf:Alt></dc:title><dc:creator><rdf:Seq><rdf:li>Bradley M. Kuhn</rdf:li></rdf:Seq></dc:creator></rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end='w'?> endstream endobj 2 0 obj <</Producer(GPL Ghostscript 8.71) /CreationDate(D:20121121130413-05'00') /ModDate(D:20121121130413-05'00') /Title(bank-statement.txt) /Author(Bradley M. Kuhn) /Creator(a2ps version 4.14)>>endobj xref 0 15 0000000000 65535 f 0000000664 00000 n 0000002601 00000 n 0000000605 00000 n 0000000445 00000 n 0000000015 00000 n 0000000426 00000 n 0000000729 00000 n 0000000914 00000 n 0000000850 00000 n 0000000976 00000 n 0000000770 00000 n 0000000800 00000 n 0000001062 00000 n 0000001120 00000 n trailer << /Size 15 /Root 1 0 R /Info 2 0 R /ID [<3CA79A8F2992E2C87D9DA71C96908F9F><3CA79A8F2992E2C87D9DA71C96908F9F>] >> startxref 2804 %%EOF ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Financial/Invoices/�����������������������������0000775�0000000�0000000�00000000000�14411236400�0026114�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Financial/Invoices/Invoice20110510.pdf����������0000664�0000000�0000000�00000033102�14411236400�0031014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%PDF-1.4 %쏢 5 0 obj <</Length 6 0 R/Filter /FlateDecode>> stream xm}Id;o"U^w3`W 5H(~=zc<?ϟ׿ WyRׇןig:5WyePu ~wO`{bJuWjO[]z%LFm&.$Z/(z*]lc *J缟hyD4ׂ^%0>Ҥe qX:(}?Qe<{vDo:F8ot+DH4 F٪M#꧷FrMctyF#f3LjEzRǜ>FCМ߀2Ad<Ԇ0yWZģy& $wEA!/7TDyJX*~{U5Fs@8*}w4 ƇH Y  (,P:<;F-(g-3g5$da30B5D[Mq:2ZعH(f^ o NI hwkYli3zgnIkq.'A X4"2ݙ)ӝ0 Z`ZR[TvğTT̿6u!C$D5v 3J;j0FF4*Rj*4_遑E 2x¦r?tGgp{<DKZ P уMYq 27J,-+7ZF<A ^F ) çgi]jvD%jӄ/(P[hט/t,ЌX;ĉ(0f6ZKǛ(v0RD/TWVkъ7T4)浕D<Ssţ5a9f`&i"S Xz *(P'GRdn^$%Xv`DG "чŢ?ZX#M:h}BuLh`%Sq~'cv<ǖr\ UoUgfօ^RF Ɯ()͑0=:fiLǐO ^P:FϵAg)Ae?58�q^ӵ"(HxIrGkC7Yl $FaTQ]J0 5(oѪƐEl/R-@V4PzM|1tA #SH,wv,<6_(;=MeZQ-<a!RPomZN+,'0&I Q%ǂmY X$L\í۠a܋bFkS@M&&* "- ',Hu /- o>(P4+h{%hC3<a)?? C&lSZ B%%̃ H\q(CM&إznS&KG9iKbSRA�K:{[,XnTQhqóyDlc~MVHOV#Kt#:2NJb@ƚ p ĀpV5uClL<kkXA; *6y8Lu!E^d'/RO [iZA ,K4ZcĝYj3Ӯ߿!;DUfXcusLD`b\w<l P˖�7;5Zp%5 CazY$IRe Kۉ|mϔ8@fbq$&G6XZxX%UnSJX4i=.̑ 2.|o yO~‚uda(4pnP@ESnTMyQS#BrA&V`8XiEmaT/8EXϾ:&Slu2ںҊǘ]*<}rJxV6Xl:)zWA /D I0 iXQ'X+ {,|1]oxFs>^"H:H`{a+FRGv]g=Qiap^t'ftk<ͩSEcHH`(ք"H7*D˒ijWHy 5h b1JY f2a7US`=S ^ @&qaHz-*EOUzBB%L+&ikJ`ȿiV35W?֓_@%.ئE‹ vā'ɑNU9up2)];#D,QۡU:6CK7\:4e{pf⋫+Y}!b6YI4ޞj.7GUGJ4h0Ɔ{18ϵأ.HTI^  &EM#W] 2{3.^k>jcuѿ7v6ʴ_l$q"r҇MA数Q,Zb<q'8IG ov'm1wT¡( ."6Ą o޴ UNLw![P?# Ye aR{Me҈#LVhskP%ػ0-5>[QW,#n?ȕMw:q:%X㴄@MkMk$5[ ّ#yBw3L劬 - .|Ѹƈ#bO ]3zĢd֓/G,2F-ŕ ~xO@!?jy,f;fqGWqӯmAYY Lwj!aH3a~yDPY@l2!jc%„QeHiz5Pdy`O_+^=$*Œ9ij 8^Mՙԁ`m`,^ īMWīyϸM/ǫWJ>ǫ�S@Ytī{?Wx]s WY*Ɣ,>+^=X])^ J`W!Iyxuvū{+^ X@Hi*|ūf@aWc~KohzhxUx$\*rJj@:BսgG hOQjopxO;x4Yъ]A73ssh~ sv9 D7㈃ͳ9h9BȉBm4$;s$X2Sz9̊(σ0AuP`^Qj_ݦCMOa+?'Bk d@uBBQSyh kWZ+w`a+ `#102/cmZ;6-$N:HM9j[&:iZ4XG a@nNƃkCʀAa߆j&xK>!xY7xqM9i>g.t�ђo`ܞreܘN!wvjl㰖MD4Kz0*SpM:h]:BHBqt~/FPfr%αtn/wPÑ!S57wCg4o 3s,߄h mSVң\~1hu%Dڛb cpD�ϷJr_a9`iYb�ei~>+4h>}Om6z3tGUGiD]#BzR ky�xgۧ2Ue&'1JB/9uU;Hd sq0ϰ,*b}a*[40nbXy a +JL.A wx lqIL ^>,8}ꨑa {U =hp^ EΟT wA]Xڐ}⇁JKK`)LUMRd{iHEU=_1MsTb XZa)lk5ms&E*Q >pu$%5",K5YakƛV xԱ-Hر~cHJSXQB@Nq%4{ vMNVKY_V0feL_3 γ]V2q+5֦2Q?neF]/+HVLR4[%K$%+Hu2A28<Va2q+֦Y5le(63<0+cgP:UeK2>FneòC!)�wuv{ϙKn5L,L4q +8M>:B|TTnot`nEN~v{z& . $yu;0ؗFy3G;qt3`7v&;FL'3=L>g.Q.&vS0)m7 E_L>fY׌3nrc22o&sebvI4u!) с1ؗ}3J{huRci_9 dt@T:UԤJDd mGπ %9{)0wwR"^< tfpJSLA|Z9lWjW_HpD-Ks)RኜmY*cMlT3š,@lT<HHp'™X"ŸƉpHې*𬥂,`Jcb`-Z:�Z׿lٯG~*c6K?wDAP 0zoT<)g f n5*&HEim&p*$@>UĻ߂ES ő`tk0!Wt 0*Qb~3\΅+{@sHTc6Y3MG2Bf.-$szs㕅h\׈!g)| %#I̼7Lk&˃$,Tv0M%ʼnUĔqCQnS@oǚ`MkwL myte<cKB1`#fStl}5wAOM#y}!؛7ZY0.LDKM W:Bf6hH4!'I-~fU8R LfGF@D@+FzK%jS:3H=/r@iu,T^S]ս|D7cm.ݑ,cw[rwD w3Ij=q &kkf''ۺZraXD [G\Ic!nFט^ >4娅*](u8L-z*¦= 6XG8Y|'LmC]+@$GɒoIM!dlSHō)hɸ@E|\=jC_Z1tۣ<K.jR `3hZ%kLj-XD`&%I "$d/1sbT&,%>ԊD> >8څxt5-U U$llyB S4OH)aVUL\_}l.U*Ib)"NH;Y"c0PI@OHO!iYFo#<K0*`#ހHKjYM/YVhxSVR6fBQs`Xuc娮VBq‚Q$ 0ޅx0klF\a_+9h9BOF[΅͗Ep +7*x&zΟ[]ܺ/0q;LtHrX8L=HMa&neq2Y2KW 6e"L D 01a$k[Q1.{}&{abuy$ h�+u q0a&sZo PDCoHea"Ha>_a>w&;LDv\s|Dx&&XD&r ! nF&ԅI9L<5HYabzQ__aac<L \a"8uDHW]v,LW9 <v2a^f°YzL~&a؋I%$/ClΧHp@V @B/Ex}>q/30epf+^f=1r,L<en$|>&WILS'`p's u2 L"( p%glfKL#Y.+0n[* փz_B}39<O4 4{!u: ނq;ޣ뒘/Y ^]a#y{zN4n=)F^çСtf3p+È-}FG'0OA8iDOczڞj=qD6"^_ew21LǨ}_'`]O?N? 蟺k?u[hm>x?uK9/R?u]4@G?uKl4qPaLB-nE)H#DV ҈`.o-<#:'ZE2k]cGEPy+ӲgͷO'JrB|HӍtR\p` 9=:Fi;*983|K0 1mqyz4Yk򂭨4714q5/q;뛢I#WFl>Ƿ:G,:F[6Xƶ`"0Ԗޛco@MbȽ=`A]\j0jīf5 ̳k]X<2yYf|"= ,BoD{l+^ѽ5WpLӍ Z.7{ &ά ȵ*VpTMBD@kڝQzS`䴛 򰗚5]|Ky&9FVȬ+-l"ݪ[\R)L`U>c/fDHfr}KH~˖z1&͡$.n:Qx˔^H"v:$ MKaXKS8pd2~(0LvRZ48tR (ɏyC4o~!M(|�4G7Ys q`\ %Z[*h hu:_iihQ#Oó?"Vb@<U<B`[q`&ݬ~WB:2ռ8!V#gxޥޠTBI8$({2g�A%kHIC]7§MLsKLsC˂z&3OT/Rn=#/cXd~z m4fgbvyi 3QB39t6qoÃlxf{.Ã#�5w 80fx&Q {dÃQdǨ Ljk6<o Rx'!i cp< 6b&lx85$ q62 4.܋Ha䇵")xxҷm?aTtys ?~;Ô {хx> Sp�=:Fif^GA@g [I'[~PHqd|Pp.^è)(`[xGpfΐnO8&&G1/dg 0$Z`~EUu@NʒE9=:FifL kÀtZaf C.~\8kճ"X;qټl^ֺ5/.'⠛ØyYki˼u5E6/yqfd^־⚵~9< ^w\׬|1Bկ-d§jBMb^E7/kq FKjGcn5/x&myXpons%#X uyܺ=˸("V.-/Rudo\<P "L h5Ital[|Fjᦎi`&o@iL38=uLE*?l;^`uw@x!w6yI t,T8W ;0z 2l%a$tCB qS7N\502B5H󨦽�,-5!* ɓ* `MNM (D& GJI`O T#''4ұl)pɗ5WNeX@NM&& 'ߚaK`wwe8v) OHmB~ǘ,E ?j(h\<r~nVeٷ-<?{\=?70+sΌ0+A'2tCp5A߀-=ENN 4g˅(-s]hnAK>YiYnARU}*g~ K+xqs&?:x ', T'`;qx 8ױX$m7vcX)R,u lZ`D/ybꃻɝ&DW#'0&X{LhaӤu)2eDbRbnH7!5 �*P�q@I*$ª; 3ES@Sh߇+lo4lN0vbD['#*îmJ'|2`+.Qb<n 0x-gE:�KԻ]TǙ׻ M3?1.Z."0.U'Q~,*R76#9]`?9֜KH ^_j \ RM``dXݜa7'5 FFi$BaXCA{\%9D5? `H)I`$SaZ^!^U5QpFtx)K<ժzqa6zNz!6{118F<pTx8i&m$GR[ƣEVXCDR|ll 7)BL$IևFˁ ;`[-Ȋ.TTj^=V_k4yJKXi QŒ!ʭ Q!JdԼ!Jh\` Z%Ŷs QǤa'W/ >ʚ' OL} _ޅ1/d Ԟޣc&o@i>[G Znw2 Qbg,eiC4$6DV=+ѳ_S%yHxC.`#>~�_|w'?=Y` ;3J;l϶8ۙ)ӱGha% ժƄ/v!J !>UMpx \9~3/ßp0^63v -I *{,H`:Imej"CQ83J1&-L0QۊguxuT")|^H0W^ܼmQ\l ̒IS7H&ػ4e-( ;FN’61k:dϋ-_QLyGQ=ĆtD=4m6uρnM LoVdLq ~WlxWTuhFFbT~r=J̩HTT< d_slL� D~ [8ƃRò�nCjlyh %}&3F|޶x=7y*[qkt4RYY\15, JRyt=zU۰m8+!# osrXr$,laZ1VC`4F(RpDr?o{hjoݯ7I .ý2tHֲ+pVVdΕ-bB,ͼK]["Pփz`2d6tߑC!k; =4 8Ni_2C6E&ˊ[tmiI[F,d-:u. >^ BeXd6Hg_u=Z^дs yt[3ӆꜢgqEH}Y*QIpdt-l|ԶU7xc__udw4=gWN oAO֫ d%7C[lWV>/~+ S)q4īȌ�o!Zӊqqn]cQe2SRC1p¸<SΗrÒ?Q>b>w<TZDazXTx&eN_sI@f9d/ gŚ*ayYyS޷dy'mI{{rRQt,s50elyӲ=%!`l)T۞ϋW˪zD:D(pX ~W?OW M2coZ:hC۴.cf"Y)>t%v+)00L : å?2"~=M!Y݊+ֶ t  m \cyVReU܄^Wɗ\U+%%}JTiVm1JB/ֈ@mԐc"=j jRg}jHE5|=jH^ʁZ5oAi^_=Bkm:FZ6uZtM6m&"ykm|m~_k1*6&ۈO絩dks}=ZkKF\ksrM}m:צctX6W_EL-1#Xp֦ ~l#UlZ66},}m~kSǨhYL&31iSCLiZ&,FWµ_"71%w0* y.1ŷ.1= QCKS%SKLOM"$[LO+").q1uʃibj@VI<vk?J:ViVu]LmML 1Y1mgoendstream endobj 6 0 obj 11261 endobj 4 0 obj <</Type/Page/MediaBox [0 0 612 792] /Rotate 0/Parent 3 0 R /Resources<</ProcSet[/PDF /Text] /ExtGState 9 0 R /Font 10 0 R >> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <</Type /Catalog /Pages 3 0 R /Metadata 11 0 R >> endobj 7 0 obj <</Type/ExtGState /OPM 1>>endobj 9 0 obj <</R7 7 0 R>> endobj 10 0 obj <</R8 8 0 R>> endobj 8 0 obj <</BaseFont/Helvetica/Type/Font /Subtype/Type1>> endobj 11 0 obj <</Type/Metadata /Subtype/XML/Length 1390>>stream <?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> <?adobe-xap-filters esc="CRLF"?> <x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'> <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:iX='http://ns.adobe.com/iX/1.0/'> <rdf:Description rdf:about='uuid:a36ff6cd-3133-11ed-0000-bbb983f47083' xmlns:pdf='http://ns.adobe.com/pdf/1.3/' pdf:Producer='GPL Ghostscript 9.05'/> <rdf:Description rdf:about='uuid:a36ff6cd-3133-11ed-0000-bbb983f47083' xmlns:xmp='http://ns.adobe.com/xap/1.0/'><xmp:ModifyDate>2012-09-07T13:08:56-05:00</xmp:ModifyDate> <xmp:CreateDate>2012-09-07T13:08:56-05:00</xmp:CreateDate> <xmp:CreatorTool>paps version 0.6.7 by Dov Grobgeld</xmp:CreatorTool></rdf:Description> <rdf:Description rdf:about='uuid:a36ff6cd-3133-11ed-0000-bbb983f47083' xmlns:xapMM='http://ns.adobe.com/xap/1.0/mm/' xapMM:DocumentID='uuid:a36ff6cd-3133-11ed-0000-bbb983f47083'/> <rdf:Description rdf:about='uuid:a36ff6cd-3133-11ed-0000-bbb983f47083' xmlns:dc='http://purl.org/dc/elements/1.1/' dc:format='application/pdf'><dc:title><rdf:Alt><rdf:li xml:lang='x-default'>Financial/Invoices/Invoice20110510.txt</rdf:li></rdf:Alt></dc:title></rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end='w'?> endstream endobj 2 0 obj <</Producer(GPL Ghostscript 9.05) /CreationDate(D:20120907130856-05'00') /ModDate(D:20120907130856-05'00') /Title(Financial/Invoices/Invoice20110510.txt) /Creator(paps version 0.6.7 by Dov Grobgeld)>>endobj xref 0 12 0000000000 65535 f 0000011585 00000 n 0000013281 00000 n 0000011526 00000 n 0000011367 00000 n 0000000015 00000 n 0000011346 00000 n 0000011650 00000 n 0000011750 00000 n 0000011691 00000 n 0000011720 00000 n 0000011814 00000 n trailer << /Size 12 /Root 1 0 R /Info 2 0 R /ID [<559119227910D69F458B3069E10D2CCE><559119227910D69F458B3069E10D2CCE>] >> startxref 13496 %%EOF ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Financial/Invoices/Invoice20110510.txt����������0000664�0000000�0000000�00000000102�14411236400�0031054�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Invoice Date: May 10, 2011 Donation to the General Fund: $50.00 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/���������������������������������������0000775�0000000�0000000�00000000000�14411236400�0024242�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Blah/����������������������������������0000775�0000000�0000000�00000000000�14411236400�0025110�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/�������������������������0000775�0000000�0000000�00000000000�14411236400�0026702�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/hosting/�����������������0000775�0000000�0000000�00000000000�14411236400�0030355�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AprilHostingReceipt.pdf�����������������������������������������������������������������������������0000664�0000000�0000000�00000006141�14411236400�0034712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/hosting�������������������������������������������������������������������������������������%PDF-1.4 %쏢 5 0 obj <</Length 6 0 R/Filter /FlateDecode>> stream xn0E~1q#]4 @;0x؆*tQ!d_Bp:;=F&='(X}]D\+cQٲ K^jyYHTTMq<q#v纹 'nn~]c$33Ah{LC %Ԃ q{þVI!JAA*kʼn1]3{f&0"Lbwɿ"XP~v9BVxu`@Yz=t1o+Wʻk/S-/RWQB"5}m?^f|}8?M*2 o 8?Qsv_RGSdrendstream endobj 6 0 obj 369 endobj 4 0 obj <</Type/Page/MediaBox [0 0 612 792] /Rotate 0/Parent 3 0 R /Resources<</ProcSet[/PDF /Text] /ExtGState 11 0 R /Font 12 0 R >> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <</Type /Catalog /Pages 3 0 R /Metadata 13 0 R >> endobj 7 0 obj <</Type/ExtGState /OPM 1>>endobj 11 0 obj <</R7 7 0 R>> endobj 12 0 obj <</R9 9 0 R/R8 8 0 R/R10 10 0 R>> endobj 9 0 obj <</BaseFont/Helvetica/Type/Font /Subtype/Type1>> endobj 8 0 obj <</BaseFont/Courier/Type/Font /Subtype/Type1>> endobj 10 0 obj <</BaseFont/Helvetica-Bold/Type/Font /Subtype/Type1>> endobj 13 0 obj <</Type/Metadata /Subtype/XML/Length 1393>>stream <?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> <?adobe-xap-filters esc="CRLF"?> <x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'> <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:iX='http://ns.adobe.com/iX/1.0/'> <rdf:Description rdf:about='1335afed-313b-11ed-0000-eb02a9a83ec4' xmlns:pdf='http://ns.adobe.com/pdf/1.3/' pdf:Producer='GPL Ghostscript 8.71'/> <rdf:Description rdf:about='1335afed-313b-11ed-0000-eb02a9a83ec4' xmlns:xmp='http://ns.adobe.com/xap/1.0/'><xmp:ModifyDate>2012-09-07T15:02:10-04:00</xmp:ModifyDate> <xmp:CreateDate>2012-09-07T15:02:10-04:00</xmp:CreateDate> <xmp:CreatorTool>a2ps version 4.14</xmp:CreatorTool></rdf:Description> <rdf:Description rdf:about='1335afed-313b-11ed-0000-eb02a9a83ec4' xmlns:xapMM='http://ns.adobe.com/xap/1.0/mm/' xapMM:DocumentID='1335afed-313b-11ed-0000-eb02a9a83ec4'/> <rdf:Description rdf:about='1335afed-313b-11ed-0000-eb02a9a83ec4' xmlns:dc='http://purl.org/dc/elements/1.1/' dc:format='application/pdf'><dc:title><rdf:Alt><rdf:li xml:lang='x-default'>receipt</rdf:li></rdf:Alt></dc:title><dc:creator><rdf:Seq><rdf:li>Bradley M. Kuhn</rdf:li></rdf:Seq></dc:creator></rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end='w'?> endstream endobj 2 0 obj <</Producer(GPL Ghostscript 8.71) /CreationDate(D:20120907150210-04'00') /ModDate(D:20120907150210-04'00') /Title(receipt) /Author(Bradley M. Kuhn) /Creator(a2ps version 4.14)>>endobj xref 0 14 0000000000 65535 f 0000000692 00000 n 0000002544 00000 n 0000000633 00000 n 0000000473 00000 n 0000000015 00000 n 0000000454 00000 n 0000000757 00000 n 0000000942 00000 n 0000000878 00000 n 0000001004 00000 n 0000000798 00000 n 0000000828 00000 n 0000001074 00000 n trailer << /Size 14 /Root 1 0 R /Info 2 0 R /ID [<346C5213A8B2262C0696706A70350365><346C5213A8B2262C0696706A70350365>] >> startxref 2736 %%EOF �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/hosting/april-invoice.pdf0000664�0000000�0000000�00000006121�14411236400�0033611�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%PDF-1.4 %쏢 5 0 obj <</Length 6 0 R/Filter /FlateDecode>> stream xN0sX"mǎcnTyI p@eyI^#g{dl88MuJ5cÌTEǹ* U3MLi :Wf{>u=ۀj˾{x2]! fxtox%Y4~�)of/Ō ز*�.ҟ>BبB#B+8}/Ho؞Ql<+_X5̖M@8dҝm7TBDu?f#dh-ߵcXjUmK],\ꀒm cSB:\OS} fendstream endobj 6 0 obj 363 endobj 4 0 obj <</Type/Page/MediaBox [0 0 612 792] /Rotate 0/Parent 3 0 R /Resources<</ProcSet[/PDF /Text] /ExtGState 11 0 R /Font 12 0 R >> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <</Type /Catalog /Pages 3 0 R /Metadata 13 0 R >> endobj 7 0 obj <</Type/ExtGState /OPM 1>>endobj 11 0 obj <</R7 7 0 R>> endobj 12 0 obj <</R9 9 0 R/R8 8 0 R/R10 10 0 R>> endobj 9 0 obj <</BaseFont/Helvetica/Type/Font /Subtype/Type1>> endobj 8 0 obj <</BaseFont/Courier/Type/Font /Subtype/Type1>> endobj 10 0 obj <</BaseFont/Helvetica-Bold/Type/Font /Subtype/Type1>> endobj 13 0 obj <</Type/Metadata /Subtype/XML/Length 1388>>stream <?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> <?adobe-xap-filters esc="CRLF"?> <x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'> <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:iX='http://ns.adobe.com/iX/1.0/'> <rdf:Description rdf:about='509a3d6e-313b-11ed-0000-dd67d504ebb3' xmlns:pdf='http://ns.adobe.com/pdf/1.3/' pdf:Producer='GPL Ghostscript 8.71'/> <rdf:Description rdf:about='509a3d6e-313b-11ed-0000-dd67d504ebb3' xmlns:xmp='http://ns.adobe.com/xap/1.0/'><xmp:ModifyDate>2012-09-07T15:03:53-04:00</xmp:ModifyDate> <xmp:CreateDate>2012-09-07T15:03:53-04:00</xmp:CreateDate> <xmp:CreatorTool>a2ps version 4.14</xmp:CreatorTool></rdf:Description> <rdf:Description rdf:about='509a3d6e-313b-11ed-0000-dd67d504ebb3' xmlns:xapMM='http://ns.adobe.com/xap/1.0/mm/' xapMM:DocumentID='509a3d6e-313b-11ed-0000-dd67d504ebb3'/> <rdf:Description rdf:about='509a3d6e-313b-11ed-0000-dd67d504ebb3' xmlns:dc='http://purl.org/dc/elements/1.1/' dc:format='application/pdf'><dc:title><rdf:Alt><rdf:li xml:lang='x-default'>cc</rdf:li></rdf:Alt></dc:title><dc:creator><rdf:Seq><rdf:li>Bradley M. Kuhn</rdf:li></rdf:Seq></dc:creator></rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end='w'?> endstream endobj 2 0 obj <</Producer(GPL Ghostscript 8.71) /CreationDate(D:20120907150353-04'00') /ModDate(D:20120907150353-04'00') /Title(cc) /Author(Bradley M. Kuhn) /Creator(a2ps version 4.14)>>endobj xref 0 14 0000000000 65535 f 0000000686 00000 n 0000002533 00000 n 0000000627 00000 n 0000000467 00000 n 0000000015 00000 n 0000000448 00000 n 0000000751 00000 n 0000000936 00000 n 0000000872 00000 n 0000000998 00000 n 0000000792 00000 n 0000000822 00000 n 0000001068 00000 n trailer << /Size 14 /Root 1 0 R /Info 2 0 R /ID [<F133486CDA2B92BB1FEE9B7DBC4AD8B9><F133486CDA2B92BB1FEE9B7DBC4AD8B9>] >> startxref 2720 %%EOF �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Foo/�����������������������������������0000775�0000000�0000000�00000000000�14411236400�0024765�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/��������������������������0000775�0000000�0000000�00000000000�14411236400�0026557�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/hosting/������������������0000775�0000000�0000000�00000000000�14411236400�0030232�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AprilHostingReceipt.pdf�����������������������������������������������������������������������������0000664�0000000�0000000�00000034735�14411236400�0034601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/hosting��������������������������������������������������������������������������������������%PDF-1.4 %쏢 5 0 obj <</Length 6 0 R/Filter /FlateDecode>> stream xu}Ke;]?GMs<�5�FƃXk#mneGJ("?/[`T)ׇ??5>5Kϟ6g\6ǧJϚ_ϳgjӟyqϟ^4G?!Qz&^gV4pI 5nSZb}VaޅG?w� 8$uNs)4ZgpU>\&i-/^̾41kmp{ԗKs3zЄ)Y:kgi&%;NRxWj%D#NrK`M~X .i2ȝXF Y1LA{''?}W dn5wL?J(vD7|]}߹mɿ1}.u&?}n8/{ʭMd}/ P&s}[3>V^wp({Uv J}<V|w8w�%ZsY{o5K)K 9N j0wOvwͭrPuy$`% I3,]ւ1d>XSVwQ(H!r^iDWT3c'wONHUkB*88H OMaw`I5'X*vx!-c)N> u@B Ot\>_<r ]3)]~% Ai%l[:tU3lc0siY x3;1{cGe-MdOcb?h=q<'ay.W”Q%M8B.3z%BOtdg(4Pw„:g`xwٕhC#@J!#RIM&bPL`|V�@T)"uPM Kp++\8UDWDITF69wߨe3NZ"C̀]%::(eE1@c.-p/{$`$]rE]].y]3u|fM]ނ}6:o[P  X+8|ϛ#0_ހҺk_w3Q (Mw n J`[":IYlƃb/CԻ=Kriut;rK]} q*VwM6 .'DP՚Fa֘]wN}}BORp)$LslhNXkF=z%.P7$2a 1pU!fA44t `qz]E9]'f_|^*.0k(Ծ"S K*tjP@B 4N \gI 8|a X:DÒiWP`4?' mv'.=Nr'J.�1EJZ0#R Co_gXA6,i5͗ҚX�( 0pwREBnەE^O'|�`u=T¦.?3 ,2a ë}sќ[Q~Yc=wc881f+iH 6) 8ˮJ GSS5?q[#CdPݷ#TZSe=aLˍ`</hc XH?a:̓ j–|a>{y!JP-j;΢[#0.\4Ӟ ʧNX_%tݘPKn\"}{L>_u80V(BnterV:l0V|0\9!A7OzW#6KP4sF:h9B(G`6> Cx@=W$d|1%êFhA4˲i/k& >!Dꪶ `9Vk}2fȿ+vO UoU+U7^(O'zVhh^CJvBAcrkyLEs`]E ч1Dy`|:8[j1J`t[,1Vt:IǕtoYWp]cKAUH}ˀ45n['ŀq YF.^5gx; {� 7Cɺ4ԓ&an؂X `G,>sY2^瞐6 ^tY F</ѷcEʖ;m[kuG[l|_ݶ,RZErUʵs+& ,rW3ijLm$ 9Z}E,D=}R #N杢FU"0 +'П/@E0xo & 3&<sK$jFCZ0]:m7PVեbq+}B-:]E60gU'IUa.f5y |,D܃VNF x&4+`RrDr13]+3P P/PXpGI 8mNcs%"ÀQx0E\frQ{ 6sn+R•C8) ./xd.k*Ē;j 5MrWD5斑H{.uTCO %ͷLCb뀧iϕ`PLF<Di,Q1|և$Pav̒Q+,RH CF%:}fHqqDsNaA6?oS ~ nJ+_ꔢk>S.~]%]8 D^cW[KRajprQhm'QǦ:20r'+~ \t /OSOJGRv;[I&}P}N�5[EA;*lUQX)<sb˧TbW`?BtmZx1SsUė4ࠝIU&,&*`VӴMaǠIzJr})Ti`rSs[0j}?GQlJI.\`MoYCQB%Ȇme"$x<+AobIH68#�5Tj|8MIj2I.=Q[8%'X';ht')#ŻSaBvdEԬUZt-Qs@tan;-!~V@- ,=Քhk!W-Holk@- Fb~5g1:VQM4E j _a{pzz)2lV Ey<RzQqcv+J\ZÅZ$F J4Xpճ%0قP'v1[]u65"qqƁ,035q/-mj '_~wk'/̢Yc\?Q:$v;B..:0sUָDӷJfm3`40BU DGWڡ&J{a-`fPbHXN9;F-2:]]f&֡8 . 4yu.5Bً,7R<[ءQwG'Y1ʃh!}$$j/;,.ڔ`ΎQWp<j>`c8zu4Igӱ.1Je9g:0戎Zck &u]fT;B sA'bq�B!e PXh%o<!A-@=I!a_pqb!_>Fg}Z q^u ]7/ѹ#&<EÉv9[BKsDt>JOŰFBIJ8/jRL5dX;y\T) ֙j";VMXK@: >z;jdb[J/O {pg2C p6Ve(266`eC lPr %Ǩ0l(PZP(JCioCi026 y~Js %H0 `PBl(2||`% l(aoCW^ü.fͺ![~ru/++sLy% .<rׅ;̋]lAX=EE=BMF.L|yqRWY[#t5@CX[ʬ\pG%,瓅T"`�eK"-+f½.x8j` HoSޫ{ Ayk2 jםGS}]XWם%z6) aׅ\Jׅl#*)Q_;^Nrg} wWka_w"K2q_/vѓ݋z~d+5{�__<#›%a S3{StS55@;6__e~1Cy7T~Q`p  鿋pݐ00ʫ%D\X4}7-nV?ru&kU !F_t.w a|.Ѻ]`uFZ}aPOUFݚt;u-fktE tƊ'D}9׼356S1jly jYfȯ0!w .U)z%A6ߛ3np}qNݿo%8a8Q8 qk0\T\ZWƩO/ƹ"Dh&Ndy /,vb�mzxB8љ0Nq#-cx881<vbgfq 6cSL:Z/딦UDt+wӵT5Y+:OI%I"PBBa6Juоpo_ f֫TPL[r .D<E͢"BdK\ @UM1+Hpӂw{d'ꉐ0V@u9%:X x<b/Z x:4po%*c}ɩ%i ).F(}'y2 bb&6YhAhb:%U#Cy *"NL;ZSDhΜa<GGFA5M8\=J.4\BH m6fӷ%>o@gŴIEvKN :KLh@L2%$ Bb.o6w<<-ay&;`˚ģ˻;-4jm |҈C1bwXv, 1LԺ`&cS$-H1x@]rlpC:_󨝉S72,#y_C{ k^ag/rZK#Nm@ l\ea ^a_,Y C;U䎽�2푅;A{1{ {%83 w=�x; M*; t3 hxA t0&ȱze}; CIg04wF!BC BbP!+y`^(@19Fah%<# h K@{-yŞ`P8`CJ7:5\AGzGSR%̀Ro'hМ!4pP f<߄my7uR xRIT�wxa:+$ ku6j-$ rOQ {J`ȿidCPuX^pK;9?4l;6;Nм@h.Nм&[m#+ DvtƙTn BOd (aA0~ ʯ^ʹC=!_B&$+fl;6dع>\FC6nNo{:g1xcP mCY| 0$Y8nC弮'$xCH>5xRш\vq`u@72^v"b_uWã('ZZ$=hSLG s9>_[ޮ9e Aɗ�:Dp-80aR/oZ[xkb~.('jӢkFHdC# 3ma R{}k6F,``򈵊G Xs (ޅI_WԋމTV3es�3T+,j% hHtVU8SW6:ٳy%-l}FC5=4P}5,1_ 52 CbEUi3@GRSk+: Aߗ!i,%xs^+|T7jl+Y2Ѕ)P^,MU, <,mbda[6qa\s)00Ԛ1f$Üt5tki`Zm%JK $#;CHJfąuc p�/L4U2{/~ԠbsF*)OpY{[Iil2c;\-E0^+cK:[IJ 0Q0 ATp iR!8 .8- E틍Z~6#ЦC[=?q P.FALpx䃈޾ϣ )ICh|ī+uo%-hT|Cxףr琣}To%>&xNk0`ݠ*rΰaSj4P{p_O Ԁ(<--$"a*o@Tj@}fΞri/ p+Z恢B/ 0|O`*{9pVG a[0K-o\X}f| هJ9Cw@cVDj5ajtJe"b#z?t5HCS%K]q)Zn檴0lb0^IarH@\]Մ]H4vPH-haϔ&#I^)k- o7D9lĢX˔0|eId*Ho/[QKcpoB:xw̅ɀ Y#*` DD хP<>)9|"腩= mq.`և'7!up]"`50BZ >^ acN\;d16*Re-s>%M RO-)32bDV2atˌ#_0iU 7G:=Xi h`ad9#6#|fުpz F c ]TДL޷3U8Ib\sI)0fn:L- !Ô)IWn&~$s/SWL<ڀc$%$<;-0޿XY_ÿyO`pAJM<h =&0;<AeO`,hCTtN(1Xu`_DhSDs$ۯ ̄@/H.f,_YJ?iCf8m ^[uRLi6a <'z7UDup3ŐlKAϵ2UeXγ͵fPh.mVUVɴLezN##rV.rLV*&|!B(;%aH:Z]NSfO{2qʌ_LvN\ּO\]SwSS#AZ>eS0v/2>M~ʰW:e�N^2Sf:ebضq/OޚS8e# ))+)?S<N)XtʀAާ̘uʌ)2S+kV]uN\uSf'3B)ד)cp2S1zLXv8e"` S(]_cT|LIR`tR0y/2:KƗLYcA\>eS1~8F%ig(cDN\ SZֹSh3O_#z[A;K8hv}+#:ŭ04yvô%/DE1YlF2QU$N11Do̓-W 1˜)W 3 6^" `;p֤֥#.GfQTz-0[%)2"fyR^d^d9sZdf)"Y~-abh +/23n]dE|w{yWzPR"QсbXf?8x.ᗞ6bhinM'1ì�R6pIzXtS]NGFޠ^3xalH QP]s񑵙B^(:^ڡ+BS5A[ ϜoxeL¨Q\݅ WFa9/M5ʣ1DY1<戜Lcb:?xQ ]vxl.Gc l1[@Tf\19sx9qw /zE#qOzX !k:I{FZx|^ц<?Ts8I\=C~uWI:2kK̢-EgnE D"6#*ͧt$ e<Ҥ|:Џ XaG8^JͭlF(l;߾1D[L sQ~T*UFOI,HXUb$!.4S*ݾ}}kcx7f]%;M�v̏6\h_ iXG#͵cmlFXiݖjkXX 0XR_,͝3}h+THew$c YfY|wM& c A9tV`:2W0,p�$P`nS:-Ʀ1E\Ș:qu ~iH%~K! (`$BJf0?F'آ,3ˌEކ.Dnń?@�XWdEOJK j~KW 1́y"/j`:QJ8kΟ$1C0j/ ;[+%j|WW7'$qyO4ԥP(@gU98J(Jx6s<]^5ݽ`Q_apW+_`XWi\@iRŒD0>0BIU5zUc^Ph;jo_`Ԗ'0ѩs2Ѱ 'XmTrj{᎗44a1U.7&*$ )$8̗S|ttWb/0ml;cpU@̒" z>S 𭁻D 4y2M a#XfHEPL"!Ze3,,n{fh/_iBFJ] PD+lD$%/Ȁt&B%hZx?[ y'[�BADng( u ڈ~`2 mʓHsii S�xskqW'X� 6ǝxȁ|.Ru\C&#.Ǻ6;w@0l|c\">Nvʛ2'TDDmN &T͕)#c~X?H?<)?G?ޓJ`͂p0c 2CHL>Bv=�c-㛻*0e^FR+cJ71~RVM Y~㰺E\XfTj%pbtU ޒOC"˔~~׀U8y~E2\Ke|M-[I o%|)TcWɂ`Jk"Y ]2)ɓK:nMa+3O{M9?᥵$0?w8ś8�g#"TK*);as{Uް + hUcš+=֡c$oA(.ݏæmJ-Qn7@\4ްYNbhŚs; -ޅi0  '>浢]_x Z⻓Dcn1\Ӫ +s> yZO˲hvُZ%!0S J!222ME=  C9 x a1T>̭8|g> Td1&RgXGh-AhofN*�G(G8M^B-8\%'a !|AS _c9~Hx~<276!Bt{j"gn'v� Q7\7/U 1<EC>#! aw@zhEULX6,Y,z6>u~s!Z~|y'w4.; ݉bG]w+m)%[\"N`M%?B;zb3aP.МhfcӝCz?>pVC wv) År-3s@h@^v-ge~OhT7/6rP"2N鮌YT3+#_Svu3͂<]6>DMwyUHo^*P w—p"z%^w> ^BGLItzZ/ӤFW y>VXJd¯<jR{aS/1tA{ӏ)ܺ>(*t⭵!!J\P~xC7FoaB0RujR)sPJ0>5`0d >֊7xU0KEo Vw߫]W6,>Zj[u_WVG#GjGEo`0zҎwl�/Cy8Cय़Q5[S q-)RޞDN(S%oK?&,K3u[؃+ Iw|.M \IjwU"lQt%(4߭+#ۘ)_qw#7>v^)TޠSL[ Kg@J<1zA W ysHEyԇi0W1)r-.N f4Q`mg(b#(E{rI.@@-"U\2i[` lō_7v)dB v XC45]bHx`a5#OqP҃!4.ٖ8`۵{-M]fEhUQb,ᅱFt"bF=$iÑ.fIaѻjHwi#ZP u5+$ctoZm>|coZ7ߛemL٧(GM_L 7oY 75!6lYFm>%ۛO?[ j.FjZ5loZ6fMtnk껨['+`{k?$2MѪNMf#`ߛFewΦQ6%ͦTaljpaM|m`SV(ͦ1Vͦ|;7cs^lU2Xy/6rl<Y氳cM`mZP$n3eMuƦl*lljlljp{i _endstream endobj 6 0 obj 12154 endobj 4 0 obj <</Type/Page/MediaBox [0 0 612 792] /Rotate 0/Parent 3 0 R /Resources<</ProcSet[/PDF /Text] /ExtGState 9 0 R /Font 10 0 R >> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <</Type /Catalog /Pages 3 0 R /Metadata 11 0 R >> endobj 7 0 obj <</Type/ExtGState /OPM 1>>endobj 9 0 obj <</R7 7 0 R>> endobj 10 0 obj <</R8 8 0 R>> endobj 8 0 obj <</BaseFont/Helvetica/Type/Font /Subtype/Type1>> endobj 11 0 obj <</Type/Metadata /Subtype/XML/Length 1405>>stream <?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> <?adobe-xap-filters esc="CRLF"?> <x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'> <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:iX='http://ns.adobe.com/iX/1.0/'> <rdf:Description rdf:about='uuid:863b284c-3133-11ed-0000-1e4000539d26' xmlns:pdf='http://ns.adobe.com/pdf/1.3/' pdf:Producer='GPL Ghostscript 9.05'/> <rdf:Description rdf:about='uuid:863b284c-3133-11ed-0000-1e4000539d26' xmlns:xmp='http://ns.adobe.com/xap/1.0/'><xmp:ModifyDate>2012-09-07T13:08:07-05:00</xmp:ModifyDate> <xmp:CreateDate>2012-09-07T13:08:07-05:00</xmp:CreateDate> <xmp:CreatorTool>paps version 0.6.7 by Dov Grobgeld</xmp:CreatorTool></rdf:Description> <rdf:Description rdf:about='uuid:863b284c-3133-11ed-0000-1e4000539d26' xmlns:xapMM='http://ns.adobe.com/xap/1.0/mm/' xapMM:DocumentID='uuid:863b284c-3133-11ed-0000-1e4000539d26'/> <rdf:Description rdf:about='uuid:863b284c-3133-11ed-0000-1e4000539d26' xmlns:dc='http://purl.org/dc/elements/1.1/' dc:format='application/pdf'><dc:title><rdf:Alt><rdf:li xml:lang='x-default'>Projects/Foo/Expenses/hosting/AprilHostingReceipt.txt</rdf:li></rdf:Alt></dc:title></rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end='w'?> endstream endobj 2 0 obj <</Producer(GPL Ghostscript 9.05) /CreationDate(D:20120907130807-05'00') /ModDate(D:20120907130807-05'00') /Title(Projects/Foo/Expenses/hosting/AprilHostingReceipt.txt) /Creator(paps version 0.6.7 by Dov Grobgeld)>>endobj xref 0 12 0000000000 65535 f 0000012478 00000 n 0000014189 00000 n 0000012419 00000 n 0000012260 00000 n 0000000015 00000 n 0000012239 00000 n 0000012543 00000 n 0000012643 00000 n 0000012584 00000 n 0000012613 00000 n 0000012707 00000 n trailer << /Size 12 /Root 1 0 R /Info 2 0 R /ID [<2ABAF442455C64448449135E321FFBF6><2ABAF442455C64448449135E321FFBF6>] >> startxref 14419 %%EOF �����������������������������������AprilHostingReceipt.txt�����������������������������������������������������������������������������0000664�0000000�0000000�00000000102�14411236400�0034624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/hosting��������������������������������������������������������������������������������������Baz Hosting Services, LLC Date: April 20, 2011 Charge: $250.00 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Foo/Invoices/��������������������������0000775�0000000�0000000�00000000000�14411236400�0026544�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Foo/Invoices/Invoice20100101.pdf�������0000664�0000000�0000000�00000035116�14411236400�0031446�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%PDF-1.4 %äüöß 2 0 obj <</Length 3 0 R/Filter/FlateDecode>> stream x}RKk0 W<HO+` Mv+vqIVRJHMO$,1M}>n^{iX֏B <ʅbETX(DͧuS[-8!\/5"[ϴi=|*T[\jJ\{`Aw3y!r1 $dLT@lzВ :)"}@R5y &,m �vԜIܾzx�㐮XgZ&< m/y<6h\\\@sd_[bLn7^~9 endstream endobj 3 0 obj 316 endobj 5 0 obj <</Length 6 0 R/Filter/FlateDecode/Length1 17880>> stream xݻy|TǕ(\nՒFZ l!aIXR I FB%c;81xL< #yϱM^' ɐd9Č V띺bq{ԩSuNUQb&3e}ij]Ӂe@um|Gq?>$DټM}bJarS%!7! Rw\eywʦEb6,?m|xsXF~;Y,1YGw'ƧG<O ~brt}c4�eFP˔ No0M;. ࢐&?45MF&.vx}7WQ3's49H~Nm$AN\}J>5=A`}.I#O<yzI;=8ob .q}{i6o�YE/b)^C+HΑa�9OJ/#:BX4M1;Ju/YE'ȶZ|aF. *rRgӗ({ %;(;=Ȗ]iZ׹6斛WwZookmY|kZxSC}]mʊ°Zj1 zV#0 5Ԗ )0rhC!�nI*YFJ)7}RP*W)A ,%K@굖P A`K77P,XE5gKK @kmזwd\Z1j,/#'&MC'8*@[o:I»MpH* LYC-jYLiWt*:98Y63"٘GB#CZߗGR%TsPTY5\;:qKHib( ],FH8؆ݿ-h۟?tf~fc( 4OI[Tۣ)1n ّrГ!9 4VMPi0?pF!Yۓ)F)TFzS4kfjݼffjdgc]^9jEJlt;~ ;쁆^6Z92Hi Q-RxZ~]avG!l8Pk2ٵ%RHfzRJ PvZOVUb$NX:}DZ~u>ZMR)ζJU-!p^=/Kd m \W{F6IM_0zF{BC \j)c]cm_O}v N~MǗaK.=zPDD ҅Q*/K=# 8TIu%K70"MˋgE f2ՁlBϕ_baFEq]5 zC[)%eQU\uPNY&Wf-^v|Dʅ~}c~<eHp+S/aSw?ϡ!ĸ𽼅o#CzhArZ^^l<yYpᮞSޓ2 DQc99ubA^VQk�Qq34pqB8~,lAn L^Ɖ Cb'j)chtyZߜk9^+$(/{cN ƑtDӍѯTRN_>ռ5}J5|p|` ex2E[BW!ߍow:ȚÚA<�h&,3Cb鲌Xh X` zrYϾxs"ʭ-+I o6U,.laeL[T^[.X(L, Kr̒Z3E0ih4_19ЏW$c$^;.- {:{QMЭNˉ4DWS[SKTy1`b#!D?A)5qAzh0mbJ0aW\bXh80L\HU<Wf@aτ|ѿ(;8m)�Z?rE?awcDԈ(Zm^FA:Q%;wj?1.n{F%EV_Znk'[]۝[Zؓ}0(8 1w[^=z0%sad- >xq ֏i7eiIݙ /qmg"рeEFt.HSoз3bR0@%tXqMG"-"Zf<'"sX/pZ<')c"ؚEqQ U"D8tF'DFod,ah GJ9߱~,bAW*#"粷Uk7@fPoU{�pݨXnz�|2{v ןɣTczj(UaJP$H UDBP%%\ҲUg0% d*k".ŵA%J}/KpUᢚq.ZTX6Z-TPܮQv_?mY*Y/IU+GrIƞ [[qhV''O+*y#{xH4nrE\õuu%Ry׷\ﹴKih 7?;J8�W^W9>1Ygr67qg* nIU;W(sY|k;{A$ET\`7~9="/"Օ#>׷\;"a[Pw:JH Ĕc%K.hJJM4?~#5$\6hZ 쿄fpzu*28a+&䱇*c;{K%+kEc ˆ[C^ԾwW̼sNsϹs+Gﳛ56|~RJG\LN* }Ԑ)y*G&AlX2VgkŪ8+ҊxK D8 $v=d hW-\Ƚ r-|�.n1ceHg;ؐ|/|+ܤ6dQ/ꄧ+߳z=+3xEd.� /՛y rQxE2*rR>&_% Va@|!:awA:vG\51MM dNetAǯ~uஇ k+_λ#OܷkplNq7}lX }/lqߙJ_vfwja78:ԐJ(:r!1=;L)7kpP-ыDhn||猷qxZvpF/='7t0/ѵkС<o9;sW'FSJEsL s|,튨Q4 1MJkt3F-LD侦:\5 a&Sg`ߒL6T 0!w1)0>WO 6/m5Ac޳_z^x /zVe)/</؈/>}[e^8^8fl ؃L"y&5^ ԕq{+p+=^Hrf/- VPov8"c.PUT\R6/,4 y˪xe8̑ 3#yr,1YbI#sA@ⶊB.xT\-_�a*qZ2 /�f¾->GM+f1 Z W3<ξ><%%-%]%L mqR"Хo�ǼT?Jtɹ7KXU PN(aJR%T%q_t#!#K8Xw)Ao gvT50`I M#(u2< ysNz3ʷy˂Ng-rkjE69 ˔ĴAma]"cuvzf^Kݞ/x2APԠa#Ap ,pl;C>F>Ӿ>zK|-X30Ҟ1oۻ9.m8S||UxRYe!;;  6js^7ikul: \4G j̊0_]1{*++yjwqfv}bDy ubץnCYʣZ܏14䦕EgӍ靹5nW %%3Jͦ]I=j=}Z;H!hǯFX T!Uk4$r΄DF1Z5E&<j4Fgß t.&N!-q=۟N`B_pKqѽ{Buzl2af0/{ᵱCENox f n\e7}J=BD d+%l ",t&&UhMgM)a:fJfMC\61S6U ͸ Ƶ j#IU7&0cƣS ͜)mM?a=2e2\Yr s(q^0ڙ$y 2Y]~+?@μvx0CA>g2o= -!J~0N^yLKf2xAf&|EO__ˀOex^GdA*&fLߒ+$pT2#gO%p7EgjQ;6?e@?m#r!3 o!ouPL IlL_4otD~Nf+e%2fᘎ({ezyL |Q/ߗ#jyLoQ۟_sYd蒡MH Ç|Nϩ)o'eDnz(|f~FIߗ|Z ,9%U e6)վqR�Tpq\\櫲 OȠ2ġN3!9%FʬٜG<U< OÈG`4ܔ|pTSȯg+9Eʊ&ZG +`DkךyB݃=Oqduu%9~dpu5qӿ57R{_3}Y4ɃIUkL"STH<m!/sp$[Zs-rWu*)7$>q`aFy5)lhc`O!)0Ƹ!FYQ㥿tpzkn/8e?D-Brҵ ;1G[h[ɨ5+㇀CR b(>V|]А<MР9lj}//p?;+IakL pi"HxS?{mUus{|gss/4g t_9G[܃at< Vy<}@! ~Ỉs#3?S* XEk:k=o`\z&'u`qJsD[a3XheD|%;%\"CE7zb,Jܔ(җwqnYNⲂL|%܋s$3:M;jnN=ԎkhCz%Bjypo.%mU.L~m6  :I@㮻_oj2PLD#noEfB,(A{PQl<qay a6Wc{m ˝W~ٟ ZsK<;.Ÿ_Z%BYsW�q>C�Ţϱz#`\>!n'&v3VיєVoqQܖ~,lɳPsaj610 ݠa]5n_GCFc1@_AF4:D!6ZBit݉pN;0s܅}WX>5W65{7$B(<p2wN8g4=ˋK,B@ ˕rP3((=h$|Ba8YnV*#'3S.Sf\5K27!'ZOϟ$Z./h{9bg^97L[\Õ/׻ V1?D*^zbv =a"cbzXpN#z1^jpnAK)KgK/ J/R\xe)(ҙC4{D}F"r�]yD1E9\.hƿ~á?`<~:N�KzͱZǞG}d-[>``]G�8;0-%Jpk!<J1:&[<QfX�0cćNgm`=(vWziƋ֠(,\IZMxֽ(AD5*AO<n'nm]=qu$2#e{|g:E7Ԯ:h ֗Vm /_v=2\;sp^o8SڱyilCH҃xiku^{kVw@ΊD,k\xɭJ^}k|hmg WE].a" 0?s qds5iTD4c#' ,*jp%qV$w?m 7.wMn']s{-7j]6c|{Qݥ1E4@ب e݄;uO2 HLmQ,Ceٲ eZ-rM,u81(&RѨY/VM5 ժT*g;{� fnϞn!HM[sjۢ?3w2ڒmj8W7X fGPSN9"g^&NlAUkѢYm�ŬNhi]:VX4.AD<DQxF.Q0傋̹qbô huuԺֽ[)A XIV0"ՋZ` A#dUۤ? #;"3*5qc)BbLmDA!,Ȁtwͽrz}p7tE<W2Q/;KٷB \jj5&,K>\B5KKK+RDQDzKT _)g(# ].dB.G;3\г7<ԣr=DJoHTݙ(&cAvmPDϣgv3^RRӮuqR3%1p@?]ع}6cp c1]@b cbp<MHL#@cV1z.Gb/1,ha *>tf=vP &c0D @a!E_|?1y^8m>$UGHպ6v*Fg>/3'>1>�v$r]*XmҘ#FQf/q1c ~Gu1F*㈪.>|dS{rr9K5QX;lDf-1&.h<�<C c[c4aMUر9RTʆ(؇1aO޴T8ϳ1b,R1-zmR 6g\'Z>봈<^cGԓYO4rc^z=_6^H9D}(:L[B|n,/HO 0 ȻKnݙ,iݕ~;m?^~;q0g% 놗zFPZYHWz Iף~6YCnfzD]&1?7.fBU~aO')P2_z ]W0۴L +ϾmV -h;2f6HS1FSݱ={ظj访o!;}~kN(�^H- <G?B&̏j^%uD!+*NKr=,9_̡`#Ve]-VlUqBl0�JI Xh6RiazKnso *f1 >TU  |Bl[} na-qw{T~}UͽLO"X(/7_L]#J50;< bF</#_#=\tj7mQkM+G=c𳈺�ԧXSAD-fWSc=ٗ-n<չxlYXUWtxjغ;FʱrStQZ+#u}k=kKqi=T~SbKݢ6u-8`uխ+t,s̵1>{~N5~؛D.xzHs)s̶8 BОc!Me9 τΆ~BXx('ރ-G=HBHYNp]t JgB?2k;Hd s=CQOP�n.xPBkf6nss }O^},wݝ~>|!%mzh]m (15ˊz5Q7IZ%- F([:3 S҂9VDw<v@YU{LyrMi"E1Ybj17 L&V.^ʤTf?,DMaIUlZGrh޶pgRZ-fZ㢮nRؿqyhi˭nm)X7rܾ֞zZKA<Qb=WzYY~AҾHKi~ON PuVk߄nUt۠!:'fO!`)3k4u6�#iaSWADGG(tzbڬ贴u.=NgohVk׃̦>z? PuiD_GE_">-ApwH}vާO^폫38#6"'@ܢ;fz9qEdXu39ZH0[4/YQOfUy?:}b$"gՓ^ꂿY}aг)g}:}Hz=? ؗ#>ԬFY_/!@sKr+ pWTe r FU+FHm*7&tMI+sc*N{S1te Fh 2xDU"5eBn%~s|7bjz?t%X8y?Nn~\Vjbw`Yٲ+7MKmTDzPLbֶEM{nj؝a9${o[kG( uDo*l@+ג9=6EPNl=@[4CA3`g2V-!06e6[(tZm־ �M�t"syY0jl6<"3LKW$_HP(vI-=-s* 6I%A+H ~aDSYZ"HLJ%xKg(2(@ $A?1{R+W9/Ʊ1zJHx/z<vw @+G)}J6aj/Kp0!ouQҍHT0R)e+$PD҆^%tXb.iZMI6qy>)"uHL˔Pq55X0U9[,Vh"A0Y}5\fEi͉/1Y䇶hD}bET#?# oDBIh6`'n|$lA݃)<O<:˩W tѬsWդ֤[vR=hr ~gO<'a~<N"ǘ!?&t$+&5D1#g5_71<f`s9dN_]We jbNȾ{+mv \-5yKr7ι9u:wI^2MASA?^_f6ѯ|4v`z>_m~?U-DAr ~4xY%X^p ޽HW 4If=:#G#o0EYJW#tr P m*vj/ oi>ޯԝח4Ub.5?k7k5e[mkd@T7y~T0%:ŒPX@,,%6YXG&_zO6+D pc+; [x [I# XYH~= SbeaFjYX fga-)`+|6ga=)6da&R/<6YBҚh-cǦ M ,>2:X1>91>94=6"l۶-Sѩ]#6fF'6un޹mh*')>Y^?:9 +}tl*0chrk`|S. >\<65=:ȱu@Նk6mUãCH<>zɱaTŧ)`CӣS۷LOOTY{,0V QM51:2:5y;]emS\-(uoOo=49ڹ8Ҏ۱;;26%p4j5`]m1*~I!Laَd; vo)4wvWYU̵NŌMd'B_~ M]YB?uLnZCVčc*s\mYJl;mRsVyg(THNJ-޶T]qZF9s<FoGmO#Q-6=ﮀuv}ެՙu[ A%٭U ͍|+Tm7MZ?Pyށk[] uOMk~Jm1#RjaPQ8,_N-;۳xk>o s:V3exaDŽّZ>k:Uzŧ\' h{.s=W qf>8%.L׼? ﳪ>%RRĥcFL?_2n/twaiݙwS2D7 &l g39_]}mq͋{^dɯ+Qzi=i*O?#K45G,'`xhCvE%R7ӭn-Wec-9hC_:58X m̯f1vZ^H\NP53 2Z5jf[V⏷m7~vO mgۘVR٦n)n 6o6۠mt:#azZtwv )x8^}))ݷ$=x,HUI%{;R#(A@?)SS$;Dv"j`*$j)25^!S?6Rqhj*gG endstream endobj 6 0 obj 12236 endobj 7 0 obj <</Type/FontDescriptor/FontName/BAAAAA+LiberationSerif /Flags 4 /FontBBox[-176 -303 1005 981]/ItalicAngle 0 /Ascent 891 /Descent -216 /CapHeight 981 /StemV 80 /FontFile2 5 0 R>> endobj 8 0 obj <</Length 388/Filter/FlateDecode>> stream x]n0 <EݡUBH--hl@F@x6ۤ@_/sea]oZ xnmmD=4z*ӍYonm^*vvƒlonbQn\ߧ 0<-tS3=7Z/ޥEc*zla 17 EVrgc]tArNJ8 i|3S|"1# ġB>2Y"q|#N%0쟢d#g$G)?:#g<"S db?^0W6WmU<)2w{E|fßnkjVo?OY}H endstream endobj 9 0 obj <</Type/Font/Subtype/TrueType/BaseFont/BAAAAA+LiberationSerif /FirstChar 0 /LastChar 37 /Widths[365 333 500 500 500 277 443 443 250 500 500 500 610 277 722 500 277 500 250 722 333 610 889 666 500 443 277 500 722 777 500 500 556 389 500 556 943 250 ] /FontDescriptor 7 0 R /ToUnicode 8 0 R >> endobj 10 0 obj <</F1 9 0 R >> endobj 11 0 obj <</Font 10 0 R /ProcSet[/PDF/Text] >> endobj 1 0 obj <</Type/Page/Parent 4 0 R/Resources 11 0 R/MediaBox[0 0 612 792]/Group<</S/Transparency/CS/DeviceRGB/I true>>/Contents 2 0 R>> endobj 4 0 obj <</Type/Pages /Resources 11 0 R /MediaBox[ 0 0 612 792 ] /Kids[ 1 0 R ] /Count 1>> endobj 12 0 obj <</Type/Catalog/Pages 4 0 R /OpenAction[1 0 R /XYZ null null 0] /ViewerPreferences<</FitWindow true >> /Lang(en-US) >> endobj 13 0 obj <</Author<FEFF0042007200610064006C006500790020004B00750068006E> /Creator<FEFF005700720069007400650072> /Producer<FEFF004F00700065006E004F00660066006900630065002E006F0072006700200033002E0032> /CreationDate(D:20120907132418-04'00')>> endobj xref 0 14 0000000000 65535 f 0000013815 00000 n 0000000019 00000 n 0000000406 00000 n 0000013958 00000 n 0000000426 00000 n 0000012747 00000 n 0000012769 00000 n 0000012963 00000 n 0000013420 00000 n 0000013728 00000 n 0000013760 00000 n 0000014057 00000 n 0000014193 00000 n trailer <</Size 14/Root 12 0 R /Info 13 0 R /ID [ <DF7630076C26DABFE2912876A9D5E12B> <DF7630076C26DABFE2912876A9D5E12B> ] /DocChecksum /0C1E7C3FAE114F1E1399EB1587F95B50 >> startxref 14442 %%EOF ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/Projects/Foo/earmark-record.txt�����������������0000664�0000000�0000000�00000000076�14411236400�0030427�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������I, Another J. Donor, would like $400 to be earmarked for Foo! ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/non-profit-test-data.ledger���������������������0000664�0000000�0000000�00000001644�14411236400�0027621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2010/01/01 Kindly T. Donor Income:Foo:Donation $-100.00 ;Invoice: Projects/Foo/Invoices/Invoice20100101.pdf Assets:Checking $100.00 2011/03/15 Another J. Donor Income:Foo:Donation $-400.00 ;Approval: Projects/Foo/earmark-record.txt Assets:Checking $400.00 2011/04/20 (1) Baz Hosting Services, LLC Expenses:Foo:Hosting $250.00 ;Receipt: Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf Assets:Checking $-250.00 2011/05/10 Donation to General Fund Income:Donation $-50.00 ;Invoice: Financial/Invoices/Invoice20110510.pdf Assets:Checking $50.00 2011/04/20 (2) Baz Hosting Services, LLC Expenses:Blah:Hosting $250.00 ;Receipt: Projects/Blah/Expenses/hosting/AprilHostingReceipt.pdf ;Invoice: Projects/Blah/Expenses/hosting/april-invoice.pdf Assets:Checking $-250.00 ;Statement: Financial/BankStuff/bank-statement.pdf ��������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/non-profit-test-data_MANIFEST�������������������0000664�0000000�0000000�00000000562�14411236400�0027644�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������chart-of-accounts.csv general-ledger.txt general-ledger.csv Financial/BankStuff/bank-statement.pdf Financial/Invoices/Invoice20110510.pdf Projects/Foo/Invoices/Invoice20100101.pdf Projects/Foo/earmark-record.txt Projects/Blah/Expenses/hosting/AprilHostingReceipt.pdf Projects/Blah/Expenses/hosting/april-invoice.pdf Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf ����������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/non-profit-test-data_chart-of-accounts.csv������0000664�0000000�0000000�00000000256�14411236400�0032550�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������"CHART OF ACCOUNTS","BEGINNING:","2012/03/01","ENDING:","2012/02/29" "Assets:Checking" "Income:Donation" "Income:Foo:Donation" "Expenses:Blah:Hosting" "Expenses:Foo:Hosting" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/tests/non-profit-test-data_general-ledger.ods���������0000664�0000000�0000000�00000013212�14411236400�0032073�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PK����R$BJÍ��8�����meta.xmlN  ؖHw&ޙ8YE4@-V51Ahq~4-)JKF w8h \9F|Xe>H%j :p[!(kN~Nhm_Kc 9Ni;_HR'TəkZo$@X g#9E=>N֔nPl<}]V?,*:/F?#>;iNsvU*QG g*&v >:cmuԕ4c%3ZS- ֔m0en5\J4MJ5 3۪?>$qn@um4, X>e{pix-,u'U;{pCbcoߺJ._/OPK����R$Bl9,���.������mimetype � l&чRIk8sp[x\V אM5PK����R$B�����������'���Configurations2/accelerator/current.xml�PK����R$B?%��������META-INF/manifest.xmlj0 }{maP`l58Ҽ–f_K2 |D-^h�mtZnVX}=ڇ]jQ2hȓB3�)*&@m�Yԫ},p޴<&¤5\cvgtK8:MB D$u Y{,yN<1?ߖ- ܇bp7uDMl�h<<)0cvnmPK����R$B_?���� ���content.xml]r8}pa&IU22LB-$|s$*E:nA_#1%-m8>&Öy߻/>�C!"‚ߐք{hˌ({{z4B$^2W*)(m(ze-藟9Q.Z L+]Ԣ5~52a^@`2GBDmO&AvONNdt Nm 5݆k!,>[DⰏX�1 V6{mC FkrrlzV ,,;ҝ d8*}vўR: 4tC;}]ОV}°@A�᪠I=ז2 _cд2׺ G(3e d-mκfqn$> H*7*-Fy4sO-QWA'Fe8 fa?7zsZ+fM!"aY|96F�Viq8ueГ&n1:1vS1YL`3+%!<i'ƀz}G4 Q�7 Z G"JB8s9\<,p\3놳QPܺC!Y—H_e>Ny$s>FQM` :l0K$brP x (�T#VH}*`Bk?粑> .A!!c%EUpSY(a"#<�G漳hV'ŗvWEvjiUNB£Zgא %"ʅCm/tL{ w g a}əSAܔ9;! G6nyqwu{nt{j-NX΁:յHE缢DMi&ݻ"g/~*w7ݵ;YcozULqw\u^ D8U"c*p@${%H`�Vgpt|*"FF%QVT !:j LIc^aČ!d =\q/i^kЍ+x9X bf%i: Z}|ޤepTc*Nd/;ݣzZ*F׬.6ZExGi{z~UPӯ?;JX?볺4yM2I]�.{yicݮk]h|5C=F14h}  CWăݗ,˲, lnO %@q77eLW7!#>^Ƨ7Ƨ3/_6n-zӮ'Zo|/z7>mi5\++q?Q%N.jzy/=h2g۲]$s.)ՄNMҭ }K&tv=њф&t4 jeDҽbVe{3d*:Ǒ"3j:g/=l7m܉uto%`1)Q˲=['4ݽmN"qk�F^v&ǶpMx[5ք&˺ք&5Jj73P=JSg#,٢k^LX)P,.)%ў)ڃ!MZIԬR-Y}Kfv=њUҬf4Yjٺ6573Q[]G*}LB{)t[PPK����R$Bp-s���� ���settings.xmlY[w:~_uV+bYHkm[Ts ' "'δL[:|ξKWqDɹR;T $.+ATrF uCqr7s%dIGIySM@|9"K[2"hVQFCʦZѨz7C]JTTҟDɄTG]|_ج_)ҿ$hnN;W$is`So։'Y#CA0q ;JK=1[$J!O̶a߅h:*V?m"\B/ZMVw k/#(LځJ:k6A͈:�R)ozNqz#!¸Hsy m ݥ =S"�;(!- >#FG֗+(@wZ 4GCڎa\3 \;W-޽+R/q,7h'wB?@8®;$xWF C[y$8bz=x& x`>T}P6S49K5iߎ2\ ĤQO尬:e_PȂcߡa6D' Pp]K:.„\nznX1g񷦏{CC sPvw6__"ޏ;cgS5IXU9#d%9mHCUg(/[D^0>%LE�!^,hJKN{ dȭG#tEp�GmD�=~v|+{8TWvz{H/iGLq<zo }}i\7u2nN7]ޕ~C[򹣂Q#YKa*qGK\Fwz s{ܺ)&7E[kp~3*P{=\m=r܋I`kÆ|^:D`MnW9d@ߘ\iy+6? #:GBl,,~y &1F?}XteB8Q2aǻ"L٭IPK����R$B\���� ���styles.xmlXێ6}W"Hʒ}H iNK̆"II}մEz%yvf4cN#puU:<!,ۺɻwv4%1$<.s̔'ՙb0{uK6I"7 XnTfЦSv([*nmiRaȢrܖN:-X-z)bH/[T:]0"ߜ6 (5$1ژU+?m|bjBc"5ŵa^'{es9áݥDԖv)imyyqw7]ЧYIE cDqL]j!uiR){ [];cך~+Ob(z޺QnNKeAT >"Atp',B})6ݤjĪE{fXXOOrTDOS6˞WHPZWLrʭn1U 2P Ty^x 1}[ZҺM|CJff#%S+B_+S!3\U*ǎ.uq^yS=4YIl۶\uNJ+SA9*'#L  -q)1kap!9r [_JpuWc[9иS1>磯O3K6)NV)TnJ�>\Uw^c,o- 9*g Q-3zT* Zul6)Vvq?#yai}Du ϕ L$p,`5FrJ ȉ$z\AQp$;SZ~_EP[=@>1o8Prf<C#$'cЗWJkiB`@aO݊E֍Aƭ˰X NMiEkU9Yī >AD~ֺaB'w }RtV;:_yrTxjbl]͔sHd/sclutq%G"3S}J:XM5?3~U}ga?Hɂ\g{. 7w9;WH`Wa-/eI@\xjAnv!7nIm:ɑl\]=gd`i\+v<`lệ7~)\KAg扄<&-G>耪}(??I?₋Y#cC>_yaq(چ!][=lY Oe05 vw^y=w#ѩ}�M5~RoYa7]v_D PK����R$BJÍ��8�������������������meta.xmlPK����R$Bl9,���.������������������mimetypePK����R$B�����������'���������������Configurations2/accelerator/current.xmlPK����R$B?%������������������L��META-INF/manifest.xmlPK����R$B_?���� �������������s��content.xmlPK����R$Bp-s���� ������������� ��settings.xmlPK����R$B\���� �������������A��styles.xmlPK��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/non-profit-audit-reports/unpaid-accruals-report.plx����������������������������0000775�0000000�0000000�00000007704�14411236400�0026433�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl # unpaid-acccurals-report.plx -*- Perl -*- # This report is designed to create what our accounts call a "Schedule of # accounts payable". and "Schedule of accounts receivable". # Copyright (C) 2013 Bradley M. Kuhn # # This program gives you software freedom; you can copy, modify, convey, # and/or redistribute it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program in a file called 'GPLv3'. If not, write to the: # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor # Boston, MA 02110-1301, USA. use strict; use warnings; use Math::BigFloat; use Date::Manip; my $LEDGER_CMD = "/usr/local/bin/ledger"; my $ACCT_WIDTH = 70; sub ParseNumber($) { $_[0] =~ s/,//g; return Math::BigFloat->new($_[0]); } Math::BigFloat->precision(-2); my $ZERO = Math::BigFloat->new("0.00"); my $TWO_CENTS = Math::BigFloat->new("0.02"); if (@ARGV < 2) { print STDERR "usage: $0 <START_DATE> <END_DATE> <LEDGER_OPTIONS>\n"; exit 1; } my($startDate, $endDate, @mainLedgerOptions) = @ARGV; my $err; my $formattedEndDate = UnixDate(DateCalc(ParseDate($endDate), ParseDateDelta("- 1 day"), \$err), "%Y/%m/%d"); die "Date calculation error on $endDate" if ($err); my $formattedStartDate = UnixDate(ParseDate($startDate), "%Y/%m/%d"); die "Date calculation error on $startDate" if ($err); my(@ledgerOptions) = (@mainLedgerOptions, '-V', '-X', '$', '-e', $endDate, '-F', '\"%(tag("Invoice"))\",\"%A\",\"%(date)\",\"%(payee)\",\"%22.108t\"\n', '--limit', 'tag("Invoice") !~ /^\s*$/', 'reg'); my @possibleTypes = ('Accrued:Loans Receivable', 'Accrued:Accounts Payable', 'Accrued:Accounts Receivable', 'Accrued:Expenses'); my %data; foreach my $type (@possibleTypes) { open(LEDGER_FUNDS, "-|", $LEDGER_CMD, @ledgerOptions, "/^$type/") or die "Unable to run $LEDGER_CMD @ledgerOptions: $!"; while (my $line = <LEDGER_FUNDS>) { next if $line =~ /"\<Adjustment\>"/; die "Unable to parse output line $line from @ledgerOptions" unless $line =~ /^\s*"([^"]+)","([^"]+)","([^"]+)","([^"]+)","\s*\$\s*([\-\d\.\,]+)"\s*$/; my($invoice, $account, $date, $payee, $amount) = ($1, $2, $3, $4, $5); $amount = ParseNumber($amount); push(@{$data{$type}{$invoice}{entries}}, { account => $account, date => $date, payee => $payee, amount => $amount}); $data{$type}{$invoice}{total} = $ZERO unless defined $data{$type}{$invoice}{total}; $data{$type}{$invoice}{total} += $amount; } close LEDGER_FUNDS; die "Failure on ledger command for $type: $!" unless ($? == 0); } foreach my $type (keys %data) { foreach my $invoice (keys %{$data{$type}}) { delete $data{$type}{$invoice} if abs($data{$type}{$invoice}{total}) <= $TWO_CENTS; } } foreach my $type (keys %data) { delete $data{$type} if scalar(keys %{$data{$type}}) == 0; } foreach my $type (keys %data) { print "\"SCHEDULE OF $type\"\n\"ENDING:\",\"$formattedEndDate\"\n\n", '"DATE","PAYEE","ACCOUNT","AMOUNT","INVOICE"', "\n"; foreach my $invoice (keys %{$data{$type}}) { my $vals; foreach my $vals (@{$data{$type}{$invoice}{entries}}) { print "\"$vals->{date}\",\"$vals->{payee}\",\"$vals->{account}\",\"\$$vals->{amount}\",\"link:$invoice\"\n"; } } print "pagebreak\n"; } ############################################################################### # # Local variables: # compile-command: "perl -c unpaid-accruals-report.plx" # End: ������������������������������������������������������������ledger-3.3.2/contrib/raw/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0015205�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/raw/GenerateLatexExpeneseReport.pl���������������������������������������������0000775�0000000�0000000�00000025772�14411236400�0023203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl use warnings; use strict; use Getopt::Long; # Options processing use Smart::Comments -ENV, "###"; # Ignore my dividers, and use # Smart_Comments=1 to activate use Cwd; use File::Basename; use 5.10.0; use POSIX qw(strftime); use Date::Calc qw(Add_Delta_Days); use Template; my $TT = Template->new( { POST_CHOMP => 1 } ); ###################################################################### # TODO # # DONE Meal summaries are broken for multi-week reports ###################################################################### # Options # Is this an internal report? my $ExpenseReportCode = undef; my $Internal = undef; my $SuppressMeals = 0; my $ViewAfter = 0; my $ImageDir = "."; my $Anonymize = 0; my $Help = undef; GetOptions( 'c' => \$Internal, 'm' => \$SuppressMeals, 'v' => \$ViewAfter, 'a' => \$Anonymize, 'I' => \$ImageDir, 'e:s' => \$ExpenseReportCode, 'h|help' => \$Help ); # Help defined $Help && do { print <<EOF; Usage: GenerateLatexExpenseReport.pl [OPTION] -e ERCode Options: -c Internal report -m Suppress meals -v View PDF on completion -a Anonymous, omit header/footer -I Image directory -e ER Code (AISER0001) EOF exit -1; }; die "Pass -e <ExpenseReportCode>" unless defined $ExpenseReportCode; ###################################################################### # Report items my @ItemizedExpenses; my $ItemizedTotal = 0.00; my @ItemizedReceipts; my @MealsReport; ###################################################################### # Gather required data about this expense report from the directory name # ie: ./AISER0015 20090419-20090425 AGIL2078 Shands HACMP/ # # ExpenseReportCode = AISER0015 # DateRange = 20090419-20090425 # ProjectCode = AGIL2078 # Description = Shands HACMP ###################################################################### # Remaining options # Where is the ledger binary? my $LedgerBin = "./ledger -f ./.ledger -V"; # -E Show empty accounts # -S d Sort by date # -V Convert mileage to $ my $LedgerOpts = "--no-color -S d"; my $LedgerAcct = "^Dest:Projects"; my $LedgerCriteria = "%" . "ER=$ExpenseReportCode"; # Internal report? if ( $Internal ) { # No mileage on an internal report # $LedgerCriteria .= "&!/Mileage/"; # This shouldn't matter, just don't put metadata for ER on mileage $LedgerAcct = "^Dest:Internal"; } my $CmdLine = "$LedgerBin reg $LedgerOpts -E \"$LedgerCriteria\" and ^Stub " . "--format \"%(tag('ER'))~%(tag('PROJECT'))~%(tag('NOTE'))\n\""; ### $CmdLine my @TempLine = `$CmdLine`; # Match all remaining items $TempLine[0] =~ m,^(?<Er>.*?)~ (?<Project>.*?)~ (?<Note>.*?)\s*$,x; my $ProjectCode= $+{'Project'}; my $Description= $+{'Note'}; ### $ExpenseReportCode ### $ProjectCode ### $Description ### $LedgerAcct ### $Internal ### $Anonymize ### $LedgerAcct ### $LedgerOpts ### $LedgerCriteria ###################################################################### # Pull main ledger report of line items for the expense report # Using ~ as a delimiter # # Example: # '2009/04/25~AR:Projects:AGIL2078:PersMealsLunch~:AISER0015: PILOT 00004259 MIDWAY, FL~ 8.68~Receipts/AGIL2078/20090425_Pilot_8_68_21204.jpg\n' # #./ledger --no-color reg %ER=AISER0040 and ^Projects -y "%Y/%m/%d" -V --format "%(date)~%(account)~%(payee)~%(amount)~%(tag('NOTE'))\n" $CmdLine = "$LedgerBin reg $LedgerOpts \"$LedgerCriteria\" and \"$LedgerAcct\" " . "-y \"%Y/%m/%d\" " . "--format \"%(date)~%(tag('CATEGORY'))~%(payee)~%(display_amount)~%(tag('NOTE'))~%(tag('RECEIPT'))\\n\""; ### $CmdLine my @MainReport = `$CmdLine`; ### MainReport: @MainReport # Remove any project codes and linefeeds #map { chomp(); s/(:AISER[0-9][0-9][0-9][0-9])+://g; } @MainReport; # No need, that's now metadata foreach my $line (@MainReport) { ### Processing Main Report... done # Remove bad chars (#&) $line =~ tr/#&//d; # Match all remaining items $line =~ m,^(?<Date>[0-9]{4}/[0-9]{2}/[0-9]{2})~ (?<Category>.*?)~ (?<Vendor>.*?)~ (?<Amount>.*?)~ (?<Note>.*?)~ (?<Receipts>.*?)\s*$,x; my %Record = %+; $Record{'Amount'}=~tr/$,//d; foreach (keys %Record) { $Record{$_} =~ s/^\s+//g; } # Grab images from <<file:///dir/filename.jpg>> my $ImageList = $Record{'Receipts'}; $ImageList //= ''; my @Images = split( /,/, $ImageList ); # Cleanup # Take last word of account name as category $Record{'Category'} = ( split( /:/, $Record{'Category'} ) )[-1]; # If no images, italicise the line item. $Record{'Italics'} = 1; # Test images foreach my $Image (@Images) { # Turn off italics because there is an image $Record{'Italics'} = 0; if (! -r $ImageDir . "/" . $Image) { print STDERR "Missing $ImageDir/$Image\n"; } } # Add to itemized expenses to be printed push( @ItemizedExpenses, \%Record ); $ItemizedTotal += $Record{'Amount'}; # Add to itemized reciepts for printing push( @ItemizedReceipts, { 'Vendor' => $Record{'Vendor'}, 'Amount' => $Record{'Amount'}, 'Date' => $Record{'Date'}, 'Images' => \@Images } ) if $ImageList; } ### @ItemizedExpenses ###################################################################### # Meals report # Summarize total spent on meals by day $CmdLine = "$LedgerBin reg $LedgerOpts " . "\"$LedgerCriteria\" and \"$LedgerAcct\" and \%CATEGORY=Meals " . "-D -n " . "--format \"%(account)~%(payee)~%(display_amount)~%(total)\n\" " . "| grep -v '<None>'"; ### $CmdLine my @MealsOutput = `$CmdLine`; ### @MealsOutput foreach my $line (@MealsOutput) { # Match all remaining items $line =~ m,^(?<Account>.*?)~ (?<DOW>.*?)~\$ (?<Amount>\s*[0-9]+\.[0-9]+)~\$ (?<RunningTotal>.*?)\s*$,x; my %TRecord=%+; $TRecord{'Account'}=~s/^Projects://g; $TRecord{'DOW'}=~s/^- //g; # Add to itemized expenses to be printed push( @MealsReport, \%TRecord ); } ###################################################################### # Total by category $CmdLine = "$LedgerBin bal $LedgerOpts " . "\"$LedgerCriteria\" and \"$LedgerAcct\" '--account=tag(\"CATEGORY\")' " . "--format \"%(account)~%(display_total)\\n\""; ### $CmdLine my @CategoryOutput = `$CmdLine`; ### @CategoryOutput my @CategoryReport; foreach my $line (@CategoryOutput) { chomp $line; $line =~ tr/\$,//d; # Match all remaining items my @Temp = split(/~/,$line); my %TRecord= ( 'Category' => $Temp[0], 'Amount' => $Temp[1]); if ($TRecord{'Category'} eq '') { $TRecord{'Category'} = '\\hline \\bf Total'; } # Cleanup # Take last word of account name as category $TRecord{'Category'} = ( split( /:/, $TRecord{'Category'} ) )[0]; # Add to itemized expenses to be printed push( @CategoryReport, \%TRecord ); } ### @CategoryReport ###################################################################### # Output ###################################################################### my $TTVars = { 'Internal' => $Internal, 'SuppressMeals' => $SuppressMeals, 'ExpenseReportCode' => $ExpenseReportCode, 'ProjectCode' => $ProjectCode, 'Description' => $Description, 'ItemizedExpenses' => \@ItemizedExpenses, 'ItemizedTotal' => $ItemizedTotal, 'MealsReport' => \@MealsReport, 'CategoryReport' => \@CategoryReport, 'ItemizedReceipts' => \@ItemizedReceipts, 'ImageDir' => $ImageDir, 'Anonymize' => $Anonymize }; #### $TTVars my $LatexTemplate = <<EOF; [% USE format %][% ToDollars = format('\\\$%.2f') %] %%%%%%%%%%% Header \\documentclass[10pt,letterpaper]{article} \\usepackage[letterpaper,includeheadfoot,top=0.5in,bottom=0.5in,left=0.75in,right=0.75in]{geometry} \\usepackage[utf8]{inputenc} \\usepackage[T1]{fontenc} \\usepackage[scaled]{helvet} \\renewcommand*\\familydefault{\\sfdefault} \\usepackage{lastpage} \\usepackage{fancyhdr} \\usepackage{graphicx} \\usepackage{multicol} \\usepackage[colorlinks,linkcolor=blue]{hyperref} \\pagestyle{fancy} \\renewcommand{\\headrulewidth}{1pt} \\renewcommand{\\footrulewidth}{0.5pt} \\geometry{headheight=48pt} \\begin{document} %%%%%%%%%% Itemized table \\section{Itemized Expenses} [% FOREACH Expense IN ItemizedExpenses %] [% IF loop.first() or ( ( loop.count mod 20 ) == 0 ) %] \\begin{tabular}{|l|l|p{2in}|r|p{2in}|} \\hline \\hline \\bf Date & \\bf Category & \\bf Expense & \\bf Amount & \\bf Notes \\\\ \\hline \\hline [% END %] [% IF Expense.Italics %]\\it[% END %] [% Expense.Date %] & [% IF Expense.Italics %]\\it[% END %] [% Expense.Category %] & [% IF Expense.Italics %]\\it[% END %] [% Expense.Vendor %] & [% IF Expense.Italics %]\\it[% END %] [% ToDollars(Expense.Amount) %] & [% IF Expense.Italics %]\\it[% END %] [% Expense.Note %] \\\\ \\hline [% IF loop.last() %] \\hline & & \\bf Total & \\bf [% ToDollars(ItemizedTotal) %] & \\\\ [% END %] [% IF ( ( (loop.count + 1) mod 20 ) == 0 ) or loop.last() %] \\hline \\hline \\end{tabular} [% IF ( ( (loop.count + 1) mod 20 ) == 0 ) %]\\newline {\\it Continued on next page...}[% END %] \\newpage [% END %] [% END %] [% IF ! Internal %][% IF ! SuppressMeals %] %%%%%%%%%% Meals summary table \\section{Meals Summary By Day} \\begin{tabular}{|l|l|p{2in}|p{2in}|} \\hline \\hline \\bf DOW & \\bf Daily Total & \\bf Running Total \\\\ \\hline \\hline [% FOREACH Meal IN MealsReport %] [% Meal.DOW %] & [% ToDollars(Meal.Amount) %] & [% ToDollars(Meal.RunningTotal) %] \\\\ \\hline [% END %] \\hline \\hline \\end{tabular} [% END %][% END %] %%%%%%%%%% Category summary \\section{Expenses Summary} \\begin{tabular}{|l|l|} \\hline \\hline \\bf Category & \\bf Total \\\\ \\hline \\hline [% FOREACH Category IN CategoryReport %] [% Category.Category %] & [% ToDollars(Category.Amount) %] \\\\ \\hline [% END %] \\hline \\hline \\end{tabular} %%%%%%%%%% Begin receipts \\section{Scanned Receipts} [% FOREACH Receipt IN ItemizedReceipts %] \\subsection{[% Receipt.Date %] [% Receipt.Vendor %]: [% ToDollars(Receipt.Amount) %]} [% FOREACH Image IN Receipt.Images %] \\includegraphics[angle=90,width=\\textwidth,keepaspectratio]{[% ImageDir %]/[% Image %]} \\\\ [% END %] [% END %] %%%%%%%%%% Footer \\end{document} EOF my $LatexFN = $ExpenseReportCode . "-" . $ProjectCode . ".tex"; ### $LatexFN $TT->process( \$LatexTemplate, $TTVars, "./tmp/" . $LatexFN ) || do { my $error = $TT->error(); print "error type: ", $error->type(), "\n"; print "error info: ", $error->info(), "\n"; die $error; }; my $LatexOutput = `pdflatex -interaction batchmode -output-directory ./tmp "$LatexFN"`; ### $LatexOutput $LatexOutput = `pdflatex -interaction batchmode -output-directory ./tmp "$LatexFN"`; ### $LatexOutput if ($ViewAfter) { my $ViewFN = $LatexFN; $ViewFN =~ s/\.tex$/.pdf/; `acroread "./tmp/$ViewFN"`; } ������ledger-3.3.2/contrib/raw/MetadataExample.dat��������������������������������������������������������0000664�0000000�0000000�00000011543�14411236400�0020737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; TAG key: value 2009/09/27 * (09/28/2009) HUDSON NEWS HOUSTN HBB HOUSTON, TX Source:Visa -$6.55 Projects:Meals ; ER: AISER0033 ; PROJECT: PROJXXXX 2009/09/27 * (09/28/2009) PEET'S COFFEE & TEA KINGWOOD, TX Source:Visa -$2.44 Projects:Meals ; ER: AISER0033 ; PROJECT: PROJXXXX 2009/09/28 * (09/29/2009) FUSIA NEW YORK, NY Source:Visa -$15.25 Projects:Meals ; ER: AISER0033 ; PROJECT: PROJXXXX 2009/09/29 * (09/30/2009) BALUCHI'S NEW YORK, NY Source:Visa Projects:Meals $20.00 ; ER: AISER0033 ; PROJECT: PROJXXXX Internal:Travel $10.44 ; ER: AISER0036 ; PROJECT: Internal 2009/10/01 * Reimbursing AISER0036 Bank:AISChecking ; ER: AISER0036 ; PROJECT: Internal Source:Visa $10.44 ---------- $ ./ledger -Ef AISER0033.dat bal '--account=tag("ER")' $-44.24 $44.24 AISER0033 0 AISER0036 -------------------- 0 $ ./ledger -Ef AISER0033.dat bal $-10.44 Bank:AISChecking $10.44 Internal:Travel $44.24 Projects:Meals $-44.24 Source:Visa -------------------- 0 $ ./ledger -Ef AISER0033.dat bal '--account=tag("PROJECT")' $-44.24 0 Internal $44.24 PROJXXXX -------------------- 0 $ ./ledger -f AISER0033.dat reg '--account=tag("PROJECT")' 09-Sep-27 HUDSON NEWS HOUSTN .. $-6.55 $-6.55 09-Sep-27 HUDSON NEWS HOUSTN .. PROJXXXX $6.55 0 09-Sep-27 PEET'S COFFEE & TEA.. $-2.44 $-2.44 09-Sep-27 PEET'S COFFEE & TEA.. PROJXXXX $2.44 0 09-Sep-28 FUSIA NEW YORK, NY $-15.25 $-15.25 09-Sep-28 FUSIA NEW YORK, NY PROJXXXX $15.25 0 09-Sep-29 BALUCHI'S NEW YORK,.. $-30.44 $-30.44 09-Sep-29 BALUCHI'S NEW YORK,.. PROJXXXX $20.00 $-10.44 09-Sep-29 BALUCHI'S NEW YORK,.. Internal $10.44 0 09-Oct-01 Reimbursing AISER0036 Internal $-10.44 $-10.44 09-Oct-01 Reimbursing AISER0036 $10.44 0 $ ./ledger -f AISER0033.dat reg '--account=tag("ER")' 09-Sep-27 HUDSON NEWS HOUSTN .. $-6.55 $-6.55 09-Sep-27 HUDSON NEWS HOUSTN .. AISER0033 $6.55 0 09-Sep-27 PEET'S COFFEE & TEA.. $-2.44 $-2.44 09-Sep-27 PEET'S COFFEE & TEA.. AISER0033 $2.44 0 09-Sep-28 FUSIA NEW YORK, NY $-15.25 $-15.25 09-Sep-28 FUSIA NEW YORK, NY AISER0033 $15.25 0 09-Sep-29 BALUCHI'S NEW YORK,.. $-30.44 $-30.44 09-Sep-29 BALUCHI'S NEW YORK,.. AISER0033 $20.00 $-10.44 09-Sep-29 BALUCHI'S NEW YORK,.. AISER0036 $10.44 0 09-Oct-01 Reimbursing AISER0036 AISER0036 $-10.44 $-10.44 09-Oct-01 Reimbursing AISER0036 $10.44 0 $ ./ledger -f AISER0033.dat reg %ER=AISER0033 09-Sep-27 HUDSON NEWS HOUSTN .. Projects:Meals $6.55 $6.55 09-Sep-27 PEET'S COFFEE & TEA.. Projects:Meals $2.44 $8.99 09-Sep-28 FUSIA NEW YORK, NY Projects:Meals $15.25 $24.24 09-Sep-29 BALUCHI'S NEW YORK,.. Projects:Meals $20.00 $44.24 $ ./ledger -f AISER0033.dat reg %ER=AISER0036 09-Sep-29 BALUCHI'S NEW YORK,.. Internal:Travel $10.44 $10.44 09-Oct-01 Reimbursing AISER0036 Bank:AISChecking $-10.44 0 $ ./ledger -f AISER0033.dat reg %PROJECT=PROJXXXX 09-Sep-27 HUDSON NEWS HOUSTN .. Projects:Meals $6.55 $6.55 09-Sep-27 PEET'S COFFEE & TEA.. Projects:Meals $2.44 $8.99 09-Sep-28 FUSIA NEW YORK, NY Projects:Meals $15.25 $24.24 09-Sep-29 BALUCHI'S NEW YORK,.. Projects:Meals $20.00 $44.24 $ ./ledger -f AISER0033.dat --prepend-format='%(tag("IMG")) ' reg %ER=0033 image1.jpg 09-Sep-27 HUDSON NEWS HOUSTN .. Projects:Meals $6.55 $6.55 image2.jpg 09-Sep-27 PEET'S COFFEE & TEA.. Projects:Meals $2.44 $8.99 image3.jpg 09-Sep-28 FUSIA NEW YORK, NY Projects:Meals $15.25 $24.24 image4.jpg 09-Sep-29 BALUCHI'S NEW YORK,.. Projects:Meals $20.00 $44.24 �������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/raw/README���������������������������������������������������������������������0000664�0000000�0000000�00000000411�14411236400�0016061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������These scripts are from my (rladams) local ledger customizations. They are intended as examples for features that can be made generic to benefit other Ledger users. As they become refined, the raw files will be removed and replaced by suitable sources in contrib. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/raw/VerifyImages.sh������������������������������������������������������������0000775�0000000�0000000�00000000273�14411236400�0020140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh grep -h '; RECEIPT: ' \ *.dat \ */*.dat \ | sed 's,\W*; RECEIPT: ,,g' \ | tr , '\n' \ | sort -u \ | while read X do [ -f "$X" ] \ && echo OK $X \ || echo XX $X done �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/raw/dotemacs.el����������������������������������������������������������������0000664�0000000�0000000�00000023071�14411236400�0017331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Ledger ;; Maybe later add this to the expense repo once it settles (add-to-list 'load-path "/home/adamsrl/.emacs.d/addons/ledger") (add-to-list 'load-path "/home/adamsrl/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell/bin") (autoload 'ledger-mode "ldg-new" nil t) (add-to-list 'auto-mode-alist '("\\.dat$" . ledger-mode)) (add-hook 'ledger-mode-hook (lambda () (setq truncate-lines 1) (url-handler-mode 1) ; Enable hyperlinks (require 'ledger-matching) ; Requires ldg-report anyway (load-file "/home/adamsrl/.emacs.d/addons/ledger/ldg-xact.el") (let ((map (current-local-map))) (define-key map (kbd "\C-c o") 'find-file-at-point) ; Open images (define-key map (kbd "<f8>") 'ledger-expense-shortcut) (define-key map (kbd "M-i") 'ledger-expense-internal) (define-key map (kbd "M-o") 'ledger-expense-personal) (define-key map (kbd "M-'") 'ledger-expense-split) (define-key map (kbd "M-n") '(lambda () (interactive) (ledger-post-next-xact) (recenter) (when (get-buffer "*Receipt*") (ledger-expense-show-receipt)))) (define-key map (kbd "M-p") '(lambda () (interactive) (ledger-post-prev-xact) (recenter) (when (get-buffer "*Receipt*") (ledger-expense-show-receipt)))) (local-unset-key [tab]) ; Ideally this turns off pcomplete (local-unset-key [(control ?i)]) ; Ideally this turns off pcomplete ) ;(defface ledger-report-face-account-ok '((t (:foreground "Cyan"))) "Derp") ;(defface ledger-report-face-account-bad '((t (:foreground "Red"))) "Derp") (font-lock-add-keywords 'ledger-mode '(("Unassigned\\|Unknown\\|; RECEIPT:$" 0 'highlight prepend))) )) ;; My customizations to make receipt image matching work with ledger-report mode (add-hook 'ledger-report-mode-hook (lambda () (hl-line-mode 1) (local-set-key (kbd "<RET>") 'ledger-report-visit-source) ; Make return jump to the right txn (local-set-key (kbd "<tab>") 'ledger-report-visit-source) ; Make tab jump to the right txn (local-set-key (kbd "n") '(lambda () (interactive) (save-selected-window (next-line) (ledger-report-visit-source)))) ; Update a txn window but keep focus (local-set-key (kbd "p") '(lambda () (interactive) (save-selected-window (previous-line) (ledger-report-visit-source)))) ; Update a txn window but keep focus (local-set-key (kbd "M-r") 'ledger-receipt-matching) ; Link receipt to current item (local-set-key (kbd "M-l") 'ledger-matching-tie-receipt-to-txn) ; Link receipt to current item (local-set-key (kbd "M-n") '(lambda () (interactive) (ledger-matching-image-offset-adjust 1))) ; Next receipt image (local-set-key (kbd "M-p") '(lambda () (interactive) (ledger-matching-image-offset-adjust -1))) ; prev receipt image (local-set-key (kbd "M-s") '(lambda () (interactive) (ledger-receipt-skip))) ; Skip receipt image (local-set-key (kbd "C-c C-e") '(lambda () (interactive) (save-selected-window (ledger-report-visit-source) (ledger-toggle-current-entry) ))) ; Toggle entry )) (defvar *ledger-expense-shortcut-ER* "Current expense report number, just last four digits (ie: 1234 results in AISER1234).") (defvar *ledger-expense-shortcut-split-ER* "Split (ie: internal) expense report number, just last four digits (ie: 1234 results in AISER1234).") (defvar *ledger-expense-shortcut-Proj* "" "Current export report project code (ie: AGIL1292)") (defun ledger-expense-shortcut-ER-format-specifier () *ledger-expense-shortcut-ER*) (defun ledger-expense-shortcut-setup (ER Split Proj) "Sets the variables expanded into the transaction." (interactive "MER Number (4 digit number only): \nMSplit ER Number (4 digit number only): \nMProject: ") (setq *ledger-expense-shortcut-ER* (concatenate 'string "AISER" ER)) (setq *ledger-expense-shortcut-split-ER* (concatenate 'string "AISER" Split)) (setq *ledger-expense-shortcut-Proj* Proj) (setq ledger-matching-project Proj) (message "Set Proj to %s and ER to %s, split to %s" *ledger-expense-shortcut-Proj* *ledger-expense-shortcut-ER* *ledger-expense-shortcut-split-ER*)) (defun ledger-expense-shortcut () "Updates the ER and Project metadata with the current values of the shortcut variables." (interactive) (when (eq major-mode 'ledger-mode) (if (or (eql *ledger-expense-shortcut-ER* "") (eql *ledger-expense-shortcut-Proj* "")) (message "Run ledger-expense-shortcut-setup first.") (save-excursion (search-forward "; ER:") (kill-line nil) (insert " " *ledger-expense-shortcut-ER*)) (save-excursion (search-forward "; PROJECT:") (kill-line nil) (insert " " *ledger-expense-shortcut-Proj*))))) (defun ledger-expense-split () "Splits the current transaction between internal and projects." (interactive) (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode (save-excursion (end-of-line) (re-search-backward "^[0-9]\\{4\\}/") (re-search-forward "^ +Dest:Projects") (move-beginning-of-line nil) (let ((begin (point)) (end (re-search-forward "^$"))) (goto-char end) (insert (buffer-substring begin end)) (goto-char end) (re-search-forward "^ Dest:Projects") (replace-match " Dest:Internal") (re-search-forward "; ER: +[A-Za-z0-9]+") (replace-match (concat "; ER: " *ledger-expense-shortcut-split-ER*) t) (when (re-search-forward "; CATEGORY: Meals" (save-excursion (re-search-forward "^$")) t) (replace-match "; CATEGORY: Travel" t)))) (re-search-backward "^[0-9]\\{4\\}/") (re-search-forward "^ +Dest:Projects") (insert-string " $") )) (defun ledger-expense-internal () "Makes the expense an internal one." (interactive) (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode (save-excursion (end-of-line) (re-search-backward "^[0-9]\\{4\\}/") (let ((begin (point)) (end (save-excursion (re-search-forward "^$")))) (when (re-search-forward "^ Dest:Projects" end t) (replace-match " Dest:Internal") ) (when (re-search-forward "; CATEGORY: Meals" (save-excursion (re-search-forward "^$")) t) (replace-match "; CATEGORY: Travel" t)))))) (defun ledger-expense-personal () "Makes the expense an personal one, eliminating metadata and receipts." (interactive) (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode (save-excursion (end-of-line) (re-search-backward "^[0-9]\\{4\\}/") (let ((begin (point)) (end (save-excursion (re-search-forward "^$")))) (when (re-search-forward "^ Dest:Projects" end t) (replace-match " Other:Personal")) (goto-char begin) (save-excursion (when (re-search-forward "^ +; ER:" end t) (beginning-of-line) (kill-line 1))) (save-excursion (when (re-search-forward "^ +; PROJECT:" end t) (beginning-of-line) (kill-line 1))) (save-excursion (when (re-search-forward "^ +; CATEGORY:" end t) (beginning-of-line) (kill-line 1))) (save-excursion (when (re-search-forward "^ +; RECEIPT:" end t) (beginning-of-line) (kill-line 1))) (ledger-toggle-current-entry))))) (defun ledger-expense-show-receipt () "Uses the Receipt buffer to show the receipt of the txn we're on." (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode (save-excursion (end-of-line) (re-search-backward "^[0-9]\\{4\\}/") (let ((begin (point)) (end (save-excursion (re-search-forward "^$")))) (save-excursion (when (re-search-forward "^\\( +; RECEIPT: +\\)\\([^,]+?.jpg\\).*$" end t) (ledger-matching-display-image (concat "/home/adamsrl/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell/" (match-string 2))) )))))) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/raw/ledger-matching.el���������������������������������������������������������0000664�0000000�0000000�00000032015�14411236400�0020562�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; This library is intended to allow me to view a receipt on one panel, and tie it to ledger transactions in another (require 'ledger-report) (defgroup ledger-matching nil "Ledger image matching") (defcustom ledger-matching-sourcedir "~/AdamsInfoServ/BusinessDocuments/Ledger/Incoming" "Source directory for images to process, ie: the incoming queue of images." :group 'ledger-matching) (defcustom ledger-matching-destdir "~/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell/Receipts" "Destination directory for images when matched, will still have a project directory appended to it." :group 'ledger-matching) (defcustom ledger-matching-relative-receipt-dir "Receipts" "Relative directory root for destination images used in Ledger entries, will have the project directory appended and receipt filename." :group 'ledger-matching) (defcustom ledger-matching-convert-binary "/usr/bin/convert" "Path to the Imagemagick convert command." :group 'ledger-matching) (defcustom ledger-matching-scale 50 "Scaling parameter to Imagemagick's convert to resize an image for viewing." :group 'ledger-matching) (defcustom ledger-matching-rotation 0 "Rotation parameter to Imagemagick's convert to rotate an image for viewing. Images on disk should always be upright for reading." :group 'ledger-matching) (defconst ledger-matching-image-buffer "*Receipt*" "Buffer name we load images into. Created if it doesn't exist, and persists across image loads.") (defvar ledger-matching-project "Internal" "The directory appended to the destination for the project code where receipts will be stored.") (defvar ledger-matching-image-offset 0 "The index of the current file from the SORTED source directory contents.") (defvar ledger-matching-image-name nil "The filename only of the current image.") (defun ledger-matching-display-image (image-filename) "Resize the image and load it into our viewing buffer." ;; Create our viewing buffer if needed, and set it. Do NOT switch, ;; this buffer isn't the primary. Let the user leave it where they ;; place it. (unless (get-buffer ledger-matching-image-buffer) (get-buffer-create ledger-matching-image-buffer)) (set-buffer ledger-matching-image-buffer) (erase-buffer) (goto-char (point-min)) (insert-string image-filename "\n") ;; Convert the source to the temporary dest applying resizing and rotation (let* ((source (expand-file-name image-filename ledger-matching-sourcedir)) (dest (make-temp-file "ledger-matching-" nil ".jpg")) (result (call-process ledger-matching-convert-binary nil (get-buffer "*Messages*") nil source "-scale" (concat (number-to-string ledger-matching-scale) "%") "-rotate" (number-to-string ledger-matching-rotation) dest))) (if (/= 0 result) ;; Bomb out if the convert fails (message "Error running convert, see *Messages* buffer for details.") ;; Insert scaled image into the viewing buffer, replacing ;; current contents Temp buffer is to force sync reading into ;; memory of the jpeg due to async race condition with display ;; and file deletion (let ((image (create-image (with-temp-buffer (insert-file-contents-literally dest) (string-as-unibyte (buffer-string))) 'jpeg t))) (insert-image image) (goto-char (point-min)) ;; Redisplay is required to prevent a race condition between displaying the image and the deletion. Apparently its async. ;; Either redisplay or the above string method work, both together can't hurt. (redisplay) )) ;; Delete our temporary file (delete-file dest))) (defun ledger-matching-update-current-image () "Grab the image from the source directory by offset and display" (let* ((file-listing (directory-files ledger-matching-sourcedir nil "\.jpg$" nil)) (len (safe-length file-listing))) ;; Ensure our offset doesn't exceed the file list (cond ((= len 0) (message "No files found in source directory.")) ((< len 0) (message "Error, list of files should never be negative. Epic fail.")) ((>= ledger-matching-image-offset len) (message "Hit end of list. Last image.") (setq ledger-matching-image-offset (1- len))) ((< ledger-matching-image-offset 0) (message "Beginning of list. First image.") (setq ledger-matching-image-offset 0))) ;; Get the name for the offset (setq ledger-matching-image-name (nth ledger-matching-image-offset file-listing)) (ledger-matching-display-image ledger-matching-image-name))) (defun ledger-matching-image-offset-adjust (amount) "Incr/decr the offset and update the receipt buffer." (setq ledger-matching-image-offset (+ ledger-matching-image-offset amount)) (ledger-matching-update-current-image)) (defun ledger-receipt-matching () "Open the receipt buffer and start with the first image." (interactive) (setq ledger-matching-image-offset 0) (ledger-matching-update-current-image)) (defun ledger-matching-tie-receipt-to-txn () (interactive) (save-selected-window (ledger-report-visit-source) ;; Assumes we're in a narrowed buffer with ONLY this txn (backward-paragraph) (beginning-of-line) ;; ;; Update the ER and Project while I'm there ;; (save-excursion ;; (search-forward "; ER:") ;; (kill-line nil) ;; (insert " " *ledger-expense-shortcut-ER*)) ;; Just do the project for now. (save-excursion (search-forward "; PROJECT:") (kill-line nil) (insert " " *ledger-expense-shortcut-Proj*)) ;; Goto the receipt line, unless their isn't one then add one (unless (search-forward "RECEIPT:" nil t) ;; Still at date line if that failed (next-line) (newline) (insert-string " ; RECEIPT:")) ;; Point immediately after : on tag ;; Check for existing jpg file (if (search-forward ".jpg" (line-end-position) t) ;; if present make it a comma delimited list (insert-string ",") ;; otherwise just add a space to pad (insert-string " ")) ;; Add our relative filename as the value of the RECEIPT tag (insert-string (concat ledger-matching-relative-receipt-dir "/" ledger-matching-project "/" ledger-matching-image-name)) ;; Create the destination project dir if it doesn't exist. (let ((full-destination (concat ledger-matching-destdir "/" ledger-matching-project ))) (unless (file-accessible-directory-p full-destination) (make-directory full-destination t))) ;; Rename the file from the source directory to its permanent home (rename-file (concat ledger-matching-sourcedir "/" ledger-matching-image-name) (concat ledger-matching-destdir "/" ledger-matching-project "/" ledger-matching-image-name)) ;; Update the receipt screen (ledger-matching-update-current-image) (message "Filed %s to project %s" ledger-matching-image-name ledger-matching-project))) (defun ledger-receipt-skip () "Move the current image to the Skip directory because its not relevant." (rename-file (concat ledger-matching-sourcedir "/" ledger-matching-image-name) (concat ledger-matching-sourcedir "/Skip/" ledger-matching-image-name)) ;; Update the receipt screen at the same offset (ledger-matching-update-current-image)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Items below are speed entry macros, and should eventually migrate to their own file. (defvar *ledger-expense-shortcut-ER* "Current expense report number, just last four digits (ie: 1234 results in AISER1234).") (defvar *ledger-expense-shortcut-split-ER* "Split (ie: internal) expense report number, just last four digits (ie: 1234 results in AISER1234).") (defvar *ledger-expense-shortcut-Proj* "" "Current export report project code (ie: AGIL1292)") (defun ledger-expense-shortcut-ER-format-specifier () *ledger-expense-shortcut-ER*) (defun ledger-expense-shortcut-Project-format-specifier () *ledger-expense-shortcut-Proj*) (defun ledger-expense-shortcut-setup (ER Split Proj) "Sets the variables expanded into the transaction." (interactive "MER Number (ER or IN and 4 digit number only): \nMSplit ER Number (ER or IN and 4 digit number only): \nMProject: ") (setq *ledger-expense-shortcut-ER* (concatenate 'string "AIS" ER)) (setq *ledger-expense-shortcut-split-ER* (concatenate 'string "AIS" Split)) (setq *ledger-expense-shortcut-Proj* Proj) (setq ledger-matching-project Proj) (message "Set Proj to %s and ER to %s, split to %s" *ledger-expense-shortcut-Proj* *ledger-expense-shortcut-ER* *ledger-expense-shortcut-split-ER*)) (defun ledger-expense-shortcut () "Updates the ER and Project metadata with the current values of the shortcut variables." (interactive) (when (eq major-mode 'ledger-mode) (if (or (eql *ledger-expense-shortcut-ER* "") (eql *ledger-expense-shortcut-Proj* "")) (message "Run ledger-expense-shortcut-setup first.") (save-excursion (search-forward "; ER:") (kill-line nil) (insert " " *ledger-expense-shortcut-ER*)) (save-excursion (search-forward "; PROJECT:") (kill-line nil) (insert " " *ledger-expense-shortcut-Proj*))))) (defun ledger-expense-split () "Splits the current transaction between internal and projects." (interactive) (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode (save-excursion (end-of-line) (re-search-backward "^[0-9]\\{4\\}/") (re-search-forward "^ +Dest:Projects") (move-beginning-of-line nil) (let ((begin (point)) (end (re-search-forward "^$"))) (goto-char end) (insert (buffer-substring begin end)) (goto-char end) (re-search-forward "^ Dest:Projects") (replace-match " Dest:Internal") (re-search-forward "; ER: +[A-Za-z0-9]+") (replace-match (concat "; ER: " *ledger-expense-shortcut-split-ER*) t) (when (re-search-forward "; CATEGORY: Meals" (save-excursion (re-search-forward "^$")) t) (replace-match "; CATEGORY: Travel" t)))) (re-search-backward "^[0-9]\\{4\\}/") (re-search-forward "^ +Dest:Projects") (insert-string " $") )) (defun ledger-expense-internal () "Makes the expense an internal one." (interactive) (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode (save-excursion (end-of-line) (re-search-backward "^[0-9]\\{4\\}/") (let ((begin (point)) (end (save-excursion (re-search-forward "^$")))) (when (re-search-forward "^ Dest:Projects" end t) (replace-match " Dest:Internal") ) (when (re-search-forward "; CATEGORY: Meals" (save-excursion (re-search-forward "^$")) t) (replace-match "; CATEGORY: Travel" t)))))) (defun ledger-expense-personal () "Makes the expense an personal one, eliminating metadata and receipts." (interactive) (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode (save-excursion (end-of-line) (re-search-backward "^[0-9]\\{4\\}/") (let ((begin (point)) (end (save-excursion (re-search-forward "^$")))) (when (re-search-forward "^ Dest:Projects" end t) (replace-match " Other:Personal")) (goto-char begin) (save-excursion (when (re-search-forward "^ +; ER:" end t) (beginning-of-line) (kill-line 1))) (save-excursion (when (re-search-forward "^ +; PROJECT:" end t) (beginning-of-line) (kill-line 1))) (save-excursion (when (re-search-forward "^ +; CATEGORY:" end t) (beginning-of-line) (kill-line 1))) (save-excursion (when (re-search-forward "^ +; RECEIPT:" end t) (beginning-of-line) (kill-line 1))) (ledger-toggle-current-entry))))) (defun ledger-expense-show-receipt () "Uses the Receipt buffer to show the receipt of the txn we're on." (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode (save-excursion (end-of-line) (re-search-backward "^[0-9]\\{4\\}/") (let ((begin (point)) (end (save-excursion (re-search-forward "^$")))) (save-excursion (when (re-search-forward "^\\( +; RECEIPT: +\\)\\([^,]+?.jpg\\).*$" end t) (ledger-matching-display-image (concat "/home/adamsrl/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell/" (match-string 2))) )))))) (provide 'ledger-matching) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/raw/ledger-shell-environment-functions�����������������������������������������0000664�0000000�0000000�00000005567�14411236400�0024064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Environment for ledger expenses [ $(whoami) == "adamsrl" ] \ && export LEDGER_HOME="/home/adamsrl/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell" \ || export LEDGER_HOME="/home/Heather/AdamsRussell" [ $(hostname) == "cardamom" ] \ && export LEDGER_BIN="${LEDGER_HOME}/ledger" \ || export LEDGER_BIN="${LEDGER_HOME}/ledger.exe" [ $(whoami) == "andersonll" ] \ && export LEDGER_HOME="/home/andersonll/AdamsInfoServ/Expenses" \ && export LEDGER_BIN="${LEDGER_HOME}/ledger" # Common reports alias ledger='${LEDGER_BIN} -f "${LEDGER_HOME}/.ledger" -VE ' alias ERSummary='ledger --pivot ER bal | egrep "AIS(ER|IN)[0-9]+|Unassigned"' function ERTxns() { [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return ledger reg "%ER=${1}" } function ERCategorySummary() { [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return ledger bal --pivot CATEGORY "%ER=${1}" } function ERMealSummary() { [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return ledger reg "%ER=${1}" and %CATEGORY=Meals -D } function ERMeals() { [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return ledger reg "%ER=${1}" and %CATEGORY=Meals } function ERUncleared() { [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return ledger reg "%ER=${1}" -U } function ERMissingReceipts() { [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return ledger reg "%ER=${1}" and not %RECEIPT } function ERVerify() { [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return echo "========== Uncleared txns below ==========" ERUncleared "$1" echo "========== Missing receipts below (miles and stubs ok) ==========" ERMissingReceipts "$1" echo "========== Category Summary (airline? mileage? car? hotel? ==========" ERCategorySummary "$1" echo "========== Meal summary (<\$50 / day unless otherwise specified) ==========" ERMealSummary "$1" echo "========== Account Verification (Internal vs Project ER should be ONE type) ==========" echo $1 | grep AISIN >/dev/null 2>&1 \ || { ledger reg "%ER=${1}" | grep Dest:Internal ; } \ && { ledger reg "%ER=${1}" | grep Dest:Projects ; } echo "========== Project Verification (only one project code should be listed) ==========" ledger print "%ER=${1}" | grep PROJECT | sort -u echo "========== Receipts missing ==========" ledger print "%ER=${1}" | grep -h '; RECEIPT: ' \ | sed 's,\W*; RECEIPT: ,,g' \ | tr , '\n' \ | sort -u \ | while read X ; do [ -f "${LEDGER_HOME}/${X}" ] \ || echo XX $X done } function ERListing() { ledger reg Stub --register-format="%(tag('ER')) %(tag('NOTE'))\n" | sort -u } function ERQueue() { ledger reg %ER=Unassigned --prepend-format="%(filename) " } �����������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/repl.sh������������������������������������������������������������������������0000775�0000000�0000000�00000000354�14411236400�0015717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash EXEC=$(which ledger) if [[ -z "$EXEC" ]]; then EXEC=$HOME/Products/ledger/ledger fi if [[ ! -x "$EXEC" ]]; then echo Cannot find Ledger executable exit 1 fi LESS=--quit-if-one-screen exec $EXEC --pager less "$@" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/report�������������������������������������������������������������������������0000775�0000000�0000000�00000001032�14411236400�0015651�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # This script facilities plotting of a ledger register report. If you # use OS/X, and have AquaTerm installed, you will probably want to set # LEDGER_TERM to "aqua". # # Examples of use: # # report -j -M reg food # plot monthly food costs # report -J reg checking # plot checking account balance if [ -z "$LEDGER_TERM" ]; then LEDGER_TERM="x11 persist" fi (cat <<EOF; ledger "$@") | gnuplot set terminal $LEDGER_TERM set xdata time set timefmt "%Y-%m-%d" plot "-" using 1:2 with lines EOF ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/tc�����������������������������������������������������������������������������0000775�0000000�0000000�00000000104�14411236400�0014743�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh timeclock out proj="$1" shift timeclock in "$proj" "$@" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/ti�����������������������������������������������������������������������������0000775�0000000�0000000�00000000065�14411236400�0014757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh proj="$1" shift timeclock in "$proj" "$@" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/to�����������������������������������������������������������������������������0000775�0000000�0000000�00000000036�14411236400�0014763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh timeclock out "$@" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/contrib/trend��������������������������������������������������������������������������0000775�0000000�0000000�00000001237�14411236400�0015461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # This script requires Python support. # # To use, just run "trend" with the accounts to compute the trend for: # # trend dining # # The trend values are not terribly meaningful, but this gives an # example of how Python can be used to create more complex reports. ledger --import-stdin -T "@rdev()" reg "$@" <<EOF import ledger mean = ledger.parse_value_expr ("AT") last_mean = None last_dev = None def rdev (details): global last_mean, last_dev mval = mean.compute (details) if last_mean is None: dev = ledger.Value () else: dev = mval - last_mean dev = (last_dev + dev) / 2 last_mean = mval last_dev = dev return dev EOF �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/default.nix����������������������������������������������������������������������������0000664�0000000�0000000�00000000163�14411236400�0015120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������(import (fetchTarball https://github.com/edolstra/flake-compat/archive/master.tar.gz) { src = ./.; }).defaultNix �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/doc/�����������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0013521�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/doc/CMakeLists.txt���������������������������������������������������������������������0000664�0000000�0000000�00000011017�14411236400�0016261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# The following will be generated or updated when the 'doc' target is built: # • user guide and man pages: if BUILD_DOCS is set # • HTML versions of the above: if BUILD_DOCS and BUILD_WEB_DOCS are set # • Doxygen / reference documentation: if USE_DOXYGEN is set ######################################################################## configure_file( ${PROJECT_SOURCE_DIR}/doc/version.texi.in ${PROJECT_BINARY_DIR}/doc/version.texi) if (USE_DOXYGEN) find_package(Doxygen) if (NOT DOXYGEN_FOUND) message(FATAL_ERROR "Could not find doxygen. Reference documentation cannot be built.") endif() configure_file(Doxyfile.in Doxyfile @ONLY) # see INPUT/FILE_PATTERNS in Doxyfile.in file(GLOB doxygen_input_files ${CMAKE_SOURCE_DIR}/src/*.h) add_custom_command(OUTPUT html/index.html COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile DEPENDS Doxyfile ${doxygen_input_files} COMMENT "Building doxygen documentation") add_custom_target(doc.doxygen DEPENDS html/index.html) else() add_custom_target(doc.doxygen) endif() ######################################################################## # BUILD_WEB_DOCS implies BUILD_DOCS if (BUILD_WEB_DOCS) set(BUILD_DOCS 1) endif() if (BUILD_DOCS) find_program(MAKEINFO makeinfo) find_program(TEXI2PDF texi2pdf) find_program(TEX tex) find_program(MAN2HTML man2html) find_program(GROFF groff) set(ledger_info_files ledger3.texi) if (NOT MAKEINFO) message(WARNING "Could not find makeinfo. Info version of documentation cannot be built.") endif() if (NOT TEXI2PDF OR NOT TEX) message(WARNING "Could not find texi2pdf or tex. PDF version of documentation will not be built.") endif() endif() ######################################################################## foreach(file ${ledger_info_files}) get_filename_component(file_base ${file} NAME_WE) if (MAKEINFO) add_custom_command(OUTPUT ${file_base}.info COMMAND makeinfo --force --no-split -o ${file_base}.info ${CMAKE_CURRENT_SOURCE_DIR}/${file} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} VERBATIM) list(APPEND ledger_doc_files ${file_base}.info) endif() if (BUILD_WEB_DOCS AND MAKEINFO) add_custom_command(OUTPUT ${file_base}.html COMMAND makeinfo --force --html --no-split -o ${file_base}.html ${CMAKE_CURRENT_SOURCE_DIR}/${file} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} VERBATIM) list(APPEND ledger_doc_files ${file_base}.html) endif() if (TEXI2PDF AND TEX) if (BUILD_A4_PDF) set(papersize --texinfo=@afourpaper) endif() add_custom_command(OUTPUT ${file_base}.pdf COMMAND texi2pdf ${papersize} -b -q -o ${file_base}.pdf ${CMAKE_CURRENT_SOURCE_DIR}/${file} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} VERBATIM) list(APPEND ledger_doc_files ${file_base}.pdf) endif() endforeach() ######################################################################## if (BUILD_WEB_DOCS) include(FindUnixCommands) if (NOT BASH) message(FATAL_ERROR "Could not find bash. Unable to build documentation.") endif() if (MAN2HTML) add_custom_command(OUTPUT ledger.1.html COMMAND ${BASH} -c "man2html ${CMAKE_CURRENT_SOURCE_DIR}/ledger.1 | tail -n+3 > ledger.1.html" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ledger.1 VERBATIM) list(APPEND ledger_doc_files ledger.1.html) elseif(GROFF) add_custom_command(OUTPUT ledger.1.html COMMAND ${BASH} -c "groff -mandoc -Thtml ${CMAKE_CURRENT_SOURCE_DIR}/ledger.1 > ledger.1.html" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ledger.1 VERBATIM) list(APPEND ledger_doc_files ledger.1.html) else() message(FATAL_ERROR "Could not find man2html or groff. HTML version of man page cannot be built.") endif() endif(BUILD_WEB_DOCS) ######################################################################## add_custom_target(doc DEPENDS ${ledger_doc_files} doc.doxygen) ######################################################################## include(GNUInstallDirs) if (CMAKE_INSTALL_MANDIR) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/ledger.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT doc) endif(CMAKE_INSTALL_MANDIR) foreach(file ${ledger_doc_files}) get_filename_component(file_ext ${file} EXT) if (file_ext STREQUAL ".info") if (CMAKE_INSTALL_INFODIR) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file} DESTINATION ${CMAKE_INSTALL_INFODIR} COMPONENT doc) endif() elseif(CMAKE_INSTALL_DOCDIR) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file} DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT doc) endif() endforeach() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/doc/Doxyfile.in������������������������������������������������������������������������0000664�0000000�0000000�00000234654�14411236400�0015652�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Doxyfile 1.8.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = Ledger # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 3.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = YES # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@/src/ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = YES # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented classes, # or namespaces to their corresponding documentation. Such a link can be # prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = YES # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command <command> <input-file>, where <command> is the value of # the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. # jww (2009-01-31): Enable this toward the end WARN_IF_UNDOCUMENTED = NO # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. # please update dependencies in CMakeList.txt if you change this INPUT = @PROJECT_SOURCE_DIR@/src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl # please update dependencies in CMakeList.txt if you change this FILE_PATTERNS = *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command <filter> <input-file>, where <filter> # is the value of the INPUT_FILTER tag, and <input-file> is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page (index.html). # This can be useful if you have a project on for instance GitHub and want reuse # the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefore more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> # Qt Help Project / Custom Filters</a>. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> # Qt Help Project / Filter Attributes</a>. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search engine # library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through other # doxygen projects that are not otherwise connected via tags files, but are # all added to the same search index. Each project needs to have a tag file set # via GENERATE_TAGFILE. The search mapping then maps the name of the tag file # to a relative location where the documentation can be found, # similar to the # TAGFILES option but without actually processing the tag file. # The format is: EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 11 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = jpg # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES ������������������������������������������������������������������������������������ledger-3.3.2/doc/GLOSSARY.md������������������������������������������������������������������������0000664�0000000�0000000�00000022575�14411236400�0015321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ACCOUNTING GLOSSARY --- Accounting and bookkeeping represent an entire field of human effort and has evolved its own specialized vocabulary. Accounting hopes to summarize and add understanding to where the money is going. **Account**: A category for grouping together amounts from similar transactions. Each account has a name, which is usually capitalized, and an account type. Accounts are often organized into a hierarchy when it helps understanding. For example, a coffee shop might have Coffee, Merchandise, and Equipment as accounts but arranged under an Inventory account because different decisions are made on the total inventory rather than just coffee. A hierarchy can be part of the account name in Ledger, e.g., "Assets:Inventory:Coffee". Note that the Ledger software usually creates the list of accounts on the fly: accounts are created when transactions use them. **Account Type**: Each account has a type of Asset, Liability, Equity, Income, or Expense. Assets represent something owned, e.g., Cash or Inventory. Liabilities represent sometime owed, e.g., a Loan or Mortgage. Equity, also called capital, is everything owned minus everything owed (Assets - Liabilities). It is the financial measure of how much you are ahead. Income is money earned somewhere, which puts you more ahead. Expenses is money spent somewhere, which puts you less ahead. The type of account determines if a debit represents an increase or decrease in an account. For example, Inventory is an asset so a transaction debiting Inventory would increase its value. Assets and Expenses increase with debits and decrease with credits; Liabilities, Equity, and Income increase with credits and decrease with debits. **Journal**: A record of all the financial transactions of a person or firm. This data of where money goes can be collated into reports. This used to be done with a physical book, called a ledger, where each account was on one page. Each debit or credit in the journal was transferred to the appropriate account page and the pages were totaled to produce reports. This process is now done with the Ledger software which creates reports from the journal. A journal is sometimes called a register. **Posting**: A single debit or credit line of a transaction. A posting comprises an account and the debit or credit amount. It also inherits the shared description and date from the transaction. In the Ledger software, a posting may also have metadata and an account state. **Report**: A summary made from a journal of transactions. Each transaction affects accounts and those effects are collated and totaled. The two most common reports are the balance sheet, which shows what is owned and owed on a specific date, and the cash flow statement, which shows how money was earned and spent over a period. The cash flow statement is also called a profit and loss statement or an income statement. **Transaction**: Our financial lives are recorded as a series of transactions. Each transaction has a specific date, an equal total of debits and credits affecting accounts, and some sort of description. For example, "On January 1, pay $100 with check #243 from Checking to Utilities for my Verizon phone bill" is a transaction. A credit of $100 decreases my Checking asset, while a balancing debit of $100 increases my Utility expense. A transaction needs at least two *postings*, meaning account debits or credits, but can be as complicated as humans can make finances. LEDGER GLOSSARY --- The Ledger software also has its own terms. **Automated Transaction**: a command directive that modifies subsequent transactions that match an expression. An automated transaction can add additional postings to a transaction, add metadata, or change transaction amounts. Reports can be filter postings modified or generated by an automated transaction. [§ Automated Transactions](http://www.ledger-cli.org/3.0/doc/ledger3.html#Automated-Transactions); [§ Concrete Example of Automated Transactions](http://www.ledger-cli.org/3.0/doc/ledger3.html#Concrete-Example-of-Automated-Transactions) **Command Directive**: a command in a journal file to change how subsequent lines and transactions in a journal file are processed. Command directives control processing, set default values for subsequent accounts and transactions, or override parts of subsequent transactions. A directive line begins with name of the directive and may have additional arguments or additional indented lines. The single letters *AbCDhIiNOoY* are aliased to other command directives, providing compatibility with the ancient past. The characters **'='** and **'-'** are command directives for an automatic transactions and periodic transactions, respectively. [§ Command Directives](http://www.ledger-cli.org/3.0/doc/ledger3.html#Command-Directives) **Commodity**: any currency, stock, time or resource to be tracked numerically. While many people only track money in Ledger, Ledger can track different resources and manage rules to convert between them. The system is flexible enough for the needs of very different users. Some track billable time, converting minutes and hours into dollars. Others track multiple currencies. Still others track the purchase and sale of stocks. Each commodity is separate unless a conversion rule is given. [§ Commodities and Currencies](http://www.ledger-cli.org/3.0/doc/ledger3.html#Commodities-and-Currencies); [§ Currencies and Commodities](http://www.ledger-cli.org/3.0/doc/ledger3.html#Currency-and-Commodities); [§ Accounts and Inventories](http://www.ledger-cli.org/3.0/doc/ledger3.html#Accounts-and-Inventories); [§ Posting Cost](http://www.ledger-cli.org/3.0/doc/ledger3.html#Posting-cost) *(and next ten sections)*; [§ Commodity Reporting](http://www.ledger-cli.org/3.0/doc/ledger3.html#Commodity-Reporting) **Effective Date**: an optional, second date information item in for a posting or transaction. Some use the effective date for when work is billed or when a check has cleared. The `--effective-date` option causes the effective date to override the transaction's initial date for that report. [§ Effective Dates](http://www.ledger-cli.org/3.0/doc/ledger3.html#Effective-Dates); **Journal File**: the text input file for ledger, sometimes called a register file. A journal file is a series of transactions, command directives, and comments. Command directives start with the single word name of the directive at the beginning of the line and include any following indented lines. Transactions start with a date a the beginning of the line and include any indented lines following. The journal file is expected to be encoded as ASCII or UTF-8 text. **Periodic Transaction**: the estimate of a transaction that would occur periodically, e.g., a monthly expense. These estimates are only used in budgeting and forecasting reports using the `--budget`, `--forecast`, or `--unbudgeted` options. [§ Budgeting and Forecasting](http://www.ledger-cli.org/3.0/doc/ledger3.html#Budgeting-and-Forecasting) **Transaction Code**: an optional item in a transaction or posting often used to record a check number or bank code. Certain custom reports can report this code. [§ Codes](http://www.ledger-cli.org/3.0/doc/ledger3.html#Codes); [§ Format Expressions](http://www.ledger-cli.org/3.0/doc/ledger3.html#Format-Expressions) **Transaction Metadata**: a term for comments and tags annotating a transaction. Comments indented with a transaction will be stored with each posting of a transaction. Tags are words in comments followed by colons. Tags can be used as filters in reports and certain tags, "Payee" or "Value", may affect fields of the transaction. [§ Metadata](http://www.ledger-cli.org/3.0/doc/ledger3.html#Metadata), [§ Applying Metadata to every matched posting](http://www.ledger-cli.org/3.0/doc/ledger3.html#Applying-metadata-to-every-matched-posting), [§ Applying Metadata to the generated posting](http://www.ledger-cli.org/3.0/doc/ledger3.html#Applying-metadata-to-the-generated-posting) **Transaction State**: a state of *cleared*, *pending*, or *uncleared* on each posting. The state is usually set for an entire transaction at once with a mark after the date. The marks are ***** (cleared), **!** (pending), or no mark (uncleared). The interpretation of this state is up to the user, but is typically used in bank reconciliations or differentiating time worked versus billed. Ledger supports reports and filters based on state. [§ Transaction State](http://www.ledger-cli.org/3.0/doc/ledger3.html#Transaction-state); [§ Cleared Report]( http://www.ledger-cli.org/3.0/doc/ledger3.html#Cleared-Report) **Virtual Posting**: an annotation posting in a transaction, similar in form as a regular posting but not required to balance debits and credits. It is often used to support [Fund Accounting](http://en.wikipedia.org/wiki/Fund_accounting) and various reports will collate and summarize virtual postings. Virtual postings should not be confused with virtual posting costs. [§ Virtual Postings](http://www.ledger-cli.org/3.0/doc/ledger3.html#Virtual-postings) [§ Working with Multiple Funds and Accounts](http://www.ledger-cli.org/3.0/doc/ledger3.html#Working-with-multiple-funds-and-accounts) �����������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/doc/grammar.y��������������������������������������������������������������������������0000664�0000000�0000000�00000012771�14411236400�0015351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * @file grammar.y * @version 3.0 * @author John Wiegley * * @brief Canonical BNF grammar for Ledger data files * * Extensions are permitted if: they are not required, and they are * backwards-compatible with this grammar. */ /* * There are three special terminals in this grammar, which violate its * context free nature: * * TEXT -- consumes all characters until the next terminal * or EOL (end of line) * WHITESPACE -- any amount of whitespace, not including EOL * STRING -- characters up to the next WHITESPACE or EOL * * BIGINT -- a number of any width, matching [0-9]+ * INT4 -- a four digit wide number * INT2 -- a two digit wide number * INT1 -- a one digit wide number * * Except for 1) the 'spacer' production (see below), 2) EOL, and 3) the * WHITESPACE required to begin a posting, whitespace is otherwise * ignored. * * Yes, this grammar is confusing and not so happy for machine readers, * but it was designed for the human author and reader. Once parsed, * the contents must be unambiguous, which means they can be output to * more rigorous formats for other programs to consume. */ /* * Journals * * A journal is a file which primarily contains xacts, among other elements. */ journal: journal_item journal | /* epsilon */ ; journal_item: whitespace directive | xact | ; whitespace: EOL | WHITESPACE EOL | ';' TEXT EOL | /* these next four are all ignored */ '*' TEXT EOL | ; directive: '@' word_directive EOL | '!' word_directive EOL | word_directive EOL | char_directive EOL ; word_directive: "include" TEXT | "account" TEXT | "end" | "alias" STRING '=' TEXT | "def" TEXT | TEXT WHITESPACE TEXT /* looked up in session (aka maybe Python) */ ; char_directive: 'i' date time TEXT | /* a timeclock.el "check in" */ 'I' date time TEXT | 'o' date time TEXT | /* a timeclock.el "check out" */ 'O' date time TEXT | 'h' TEXT EOL | 'b' TEXT EOL | 'D' amount | /* sets display parameters for a commodity */ 'A' TEXT | /* sets the "default balancing account" */ 'C' commodity '=' amount | /* specifies a commodity conversion */ 'P' date time commodity amount | /* a pricing history xact */ 'N' commodity | /* commodity's price is never downloaded */ 'Y' INT4 | /* sets the default year for date parsing */ '-' '-' STRING TEXT | /* specify command-line options in the file */ ; date: INT4 date_sep INT2 date_sep INT2 ; date_opt: '=' date | /* epsilon */ ; date_sep: '/' | '-' | '.' ; time: INT2 ':' INT2 ':' INT2 ; commodity: '"' TEXT '"' | STRING ; /* * Xacts * * Xacts are the atomic units of accounting, which are composed of * multiple postings between accounts, so long as it all balances in * the end. */ xact: plain_xact | periodic_xact | automated_xact ; plain_xact: date date_opt status_opt code_opt FULLSTRING note_opt EOL postings ; status_opt: status | /* epsilon */ ; status: '*' | '!' | /* epsilon */ ; code_opt: code | /* epsilon */ ; code: '(' TEXT ')' ; spacer: ' ' ' ' | '\t' | ' ' '\t' ; note_opt: spacer note | /* epsilon */ ; note: ';' TEXT ; /* ---------------------------------------------------------------------- */ periodic_xact: '~' period_expr note_opt EOL posting postings ; /* * A period expression has its own sub-grammar, which I don't quite have * the time to exhaustively describe now. See datetime.cc. It allows * for lots and lots of things, and is probably horribly ambiguous. */ period_expr: FULLSTRING ; /* ---------------------------------------------------------------------- */ automated_xact: '=' value_expr note_opt EOL posting postings ; /* * Value expressions are a algebraic math expressions very similar to * XPath (minus the path traversal items). This grammar needs fleshing * out also, since it's allowed in many places. */ value_expr: FULLSTRING ; /* * There is a serious ambiguity here which the parser resolves as * follows: if an amount_expr can be parsed as an amount, it's an * amount; otherwise, it's a value expression. */ quantity: neg_opt BIGINT decimal_opt ; neg_opt: '-' | /* epsilon */ ; decimal_opt: '.' BIGINT | /* epsilon */ ; annotation: lot_price_opt lot_date_opt lot_note_opt ; lot_date_opt: date | /* epsilon */ ; lot_date: '[' date ']' ; lot_price_opt: price | /* epsilon */ ; lot_price: '{' amount '}' ; lot_note_opt: note | /* epsilon */ ; lot_note: '(' string ')' ; amount: neg_opt commodity quantity annotation | quantity commodity annotation ; amount_expr: amount | value_expr ; /* * Postings * * Postings are the fundamental unit of accounting, and represent * the movement of commodities to or from an account. Thus, paying off * your credit card consists of two balancing postings: one that * withdraws money from your checking account, and another which pays * money to your credit institution. */ postings: posting postings | /* epsilon */ ; posting: WHITESPACE status_opt account values_opt note_opt EOL; account_name: FULLSTRING ; values_opt: spacer amount_expr price_opt | /* epsilon */ ; price_opt: price | /* epsilon */ ; price: '@' amount_expr | '@@' amount_expr /* in this case, it's the whole price */ ; account: account_name | '(' account_name ')' | '[' account_name ']' ; /* grammar.y ends here */ �������ledger-3.3.2/doc/ledger.1���������������������������������������������������������������������������0000664�0000000�0000000�00000116657�14411236400�0015065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.Dd March 15, 2019 .Dt LEDGER 1 .Os .Sh NAME .Nm ledger .Nd Command-line, double-entry account reporting tool .Sh SYNOPSIS .Nm .Op Ar options .Op Ar command .Op Ar arguments .Sh DESCRIPTION .Nm is a command-line accounting tool based on the power and completeness of double-entry accounting. It is only a reporting tool, which means it never modifies your data files, but it does offer a large selection of reports, and different ways to customize them to your needs. .Sh COMMANDS .Nm accepts several top-level commands, each of which generates a different kind of basic report. Most of them accept a .Ar report-query argument, in order to determine what should be reported. To understand the syntax of a .Ar report-query , see the section on .Sx QUERIES . In its most basic form, simply specifying one or more strings produces a report for all accounts containing those strings. .Pp If no command is given, .Nm enters a .Tn REPL , or command loop, allowing several commands to be executed on the same dataset without reparsing. .Pp The following is a complete list of accepted reporting commands: .Bl -tag -width accounts .It Ic accounts Oo Ar report-query Oc List all accounts for postings that match the .Ar report-query . .El .Bl -tag -width balance .It Ic balance Oo Ar report-query Oc Print a balance report showing totals for postings that match .Ar report-query , and aggregate totals for parents of those accounts. Options most commonly used with this command are: .Bl -tag -compact -width "--collapse (-n)" .It Fl \-basis Pq Fl B Report in terms of cost basis, not amount or value. This is the only form of report which is guaranteed to always balance to zero, when no .Ar report-query is specified. Only show totals for the top-most accounts. .It Fl \-empty Pq Fl E Show accounts whose total is zero. .It Fl \-flat Rather than display a hierarchical tree, flatten the report to show subtotals for only accounts matching .Ar report-query . .It Fl \-no-total Suppress the summary total shown at the bottom of the report. .El .Pp The synonyms .Ic bal and .Ic b are also accepted. .It Ic budget Oo Ar report-query Oc A special balance report which includes three extra columns: the amount budgeted during the reporting period, how spending differed from the budget, and the percentage of budget spent (exceeds 100% if you go over budget). Note that budgeting requires one or more .Do periodic transactions .Dc to be defined in your data file(s). See the manual for more information. .It Ic cleared Oo Ar report-query Oc A special balance report which adds two extra columns: the cleared balance for each account, and the date of the most recent cleared posting in that account. For this accounting to be meaningful, the cleared flag must be set on at least one posting. See the manual for more information. .It Ic commodities Oo Ar report-query Oc List all commodities for postings matching the .Ar report-query . .It Ic convert Reads data from a CSV (comma-separated values) file and generates .Nm transactions. .It Ic csv Oo Ar report-query Oc Report of postings matching the .Ar report-query in CSV format (comma-separated values). Useful for exporting data to a spreadsheet for further analysis or charting. .It Ic entry Oo Ar entry-template Oc Generate and display a new, properly formatted .Nm transaction by comparing the .Ar entry-template to the transactions in your data file(s). For more information on draft templates and using this command to quickly create new transactions, see the section .Sx ENTRIES . .Pp The synonym .Ic xact is also accepted. .It Ic emacs Oo Ar query Oc Output posting and transaction data in a format readily consumed by the Emacs editor, in a series of Lisp forms. This is used by the Emacs ledger-mode to process reporting data from .Nm . .It Ic equity Oo Ar report-query Oc Print a transaction with a series of postings that balance current totals for accounts matching the .Ar report-query in a special account called .Li Equity:Opening Balances . The purpose of this report is to close the books for a prior year, while using these equity postings to carry forward those balances. .It Ic payees Oo Ar report-query Oc List all payees for postings matching the .Ar report-query . .It Ic pricemap Produce a file which can be used to generate a graph with graphviz showing the relationship of commodities in the .Nm file. .It Ic prices Oo Ar report-query Oc Report prices for all commodities in postings matching the .Ar report-query . The prices are reported with the granularity of a single day. .It Ic pricedb Oo Ar report-query Oc Report prices for all commodities in postings matching the .Ar report-query . Prices are reported down to the second, using the same format as the .Pa ~/.pricedb file. .It Ic print Oo Ar report-query Oc Print out the full transactions of any matching postings using the same format as they would appear in a data file. This can be used to extract subsets from a .Nm file to transfer to other files. .It Ic push Oo Ar options Oc In the .Tn REPL , push a set of command-line .Ar options , so that they will apply to all subsequent reports. .It Ic pop In the .Tn REPL , pop any option settings that have been .Sm off .Ic push ed. .Sm on .It Ic register Oo Ar report-query Oc List all postings matching the .Ar report-query . This is one of the most common commands, and can be used to provide a variety of useful reports. Options most commonly used with this command are: .Pp .Bl -tag -compact -width "--collapse (-n)" .It Fl \-average Pq Fl A Show the running average, rather than a running total. .It Fl \-current Pq Fl c Don't show postings beyond the present day. .It Fl \-exchange Qo Ar COMMODITY Oo , Ar COMMODITY, ... Oc Qc Pq Fl X Render all values in the given .Ar commodity , if a price conversion rate can be determined. If multiple commodities are given, values in a listed commodity will remain as-is, and others will be displayed in the first listed commodity they can be converted to. Rates are always displayed relative to the date of the posting they are calculated for. This means a .Ic register report is a historical value report. For current values, it may be preferable to use the .Ic balance report. .It Fl \-gain Pq Fl G Show any gains (or losses) in commodity values over time. .It Fl \-head Ar number Only show the top .Ar number postings. .It Fl \-historical Pq Fl H Value commodities at the time of their acquisition. .It Fl \-invert Invert the value of amounts shown. .It Fl \-market Pq Fl V Show current market values for all amounts. This is determined in a somewhat magical fashion. It is probably more straightforward to use .Fl \-exchange option. .It Fl \-period Ar time-period Pq Fl p Show postings only for the given .Ar time-period . .It Fl \-related Pq Fl r Show postings that are related to those that would have been shown. It has the effect of displaying the .Qq other side of the postings. .It Fl \-sort Ar value-expression Pq Fl S Sort postings by evaluating the given .Ar value-expression . Note that a comma-separated list of expressions is allowed, in which case each sorting term is used in order to determine the final ordering. For example, to search by date and then amount, one would use: .Dl ledger reg --sort 'date, amount' The sort order may be controlled with the '-' sign. For example, to sort in reverse chronological order: .Dl ledger reg --sort '-date' .It Fl \-tail Ar number Only show the last .Ar number postings. .It Fl \-uncleared Pq Fl U Only show uncleared (i.e., recent) postings. .El .Pp There are also several grouping options that can be useful: .Pp .Bl -tag -compact -width "--collapse (-n)" .It Fl \-by-payee Pq Fl P Group postings by common payee names. .It Fl \-daily Pq Fl D Group postings by day. .It Fl \-weekly Pq Fl W Group postings by week (starting on Sundays). .It Fl \-start-of-week Ar day Set the start of each report grouped by week to the given .Ar day . .It Fl \-monthly Pq Fl M Group postings by month. .It Fl \-quarterly Group postings by fiscal quarter. .It Fl \-yearly Pq Fl Y Group postings by year. .It Fl \-days-of-week Group postings by the day of the week on which they took place. .It Fl \-subtotal Pq Fl s Group all postings together. This is very similar to the totals shown by the .Ic balance report. .El .Pp The synonyms .Ic reg and .Ic r are also accepted. .It Ic select Oo Ar sql-query Oc List all postings matching the .Ar sql-query . This command allows to generate SQL-like queries, e.g.: .Dl Li ledger select date,amount from posts where account=~/Income/ .It Ic source Parse a journal file and checks it for errors. .Nm will return success if no errors are found. .It Ic stats Oo Ar report-query Oc Provide summary information about all the postings matching .Ar report-query . It provides information such as: .Bl -bullet -offset indent -compact .It Time range of all matching postings .It Unique payees .It Unique accounts .It Postings total .It Uncleared postings .It Days since last posting .It Posts in the last 7 days .It Posts in the last 30 days .It Posts this month .El .It Ic xml Oo Ar report-query Oc Output data relating to the current report in .Tn XML format. It includes all accounts and commodities involved in the report, plus the postings and the transactions they are contained in. See the manual for more information. .El .Sh OPTIONS .Bl -tag -width -indent .It Fl \-abbrev-len Ar INT Set the minimum length an account can be abbreviated to if it doesn't fit inside the .Sy account-width . If .Ar INT is zero, then the account name will be truncated on the right. If .Ar INT is greater than .Sy account-width then the account will be truncated on the left, with no shortening of the account names in order to fit into the desired width. .It Fl \-account Ar EXPR Prepend .Ar EXPR to all accounts reported. That is, the option .Fl \-account Ar \*q'Personal'\*q would tack .Ar Personal: and .Fl \-account Ar \*qtag('VAT')\*q would tack the value of the VAT tag to the beginning of every account reported in a .Ic balance or .Ic register report. .It Fl \-account-width Ar INT Set the width of the account column in the .Ic register report to .Ar INT characters. .It Fl \-actual Pq Fl L Report only real transactions, with no automated or virtual transactions used. .It Fl \-add-budget Show only un-budgeted postings. .It Fl \-amount Ar EXPR Pq Fl t Apply the given value expression to the posting amount. Using .Fl \-amount Ar EXPR you can apply an arbitrary transformation to the postings. .It Fl \-amount-data Pq Fl j On a register report print only the dates and amount of postings. Useful for graphing and spreadsheet applications. .It Fl \-amount-width Ar INT Set the width in characters of the amount column in the .Ic register report. .It Fl \-anon Anonymize registry output, mostly for sending in bug reports. .It Fl \-ansi Use color if the terminal supports it. Alias for .Fl \-color .It Fl \-args-only Ignore init files and environment variables for the .Nm run. .It Fl \-auto-match When generating a ledger transaction from a CSV file using the .Ic convert command, automatically match an account from the Ledger journal. .It Fl \-aux-date Show auxiliary dates for all calculations. Alias for .Fl \-effective .It Fl \-average Pq Fl A Print average values over the number of transactions instead of running totals. .It Fl \-average-lot-prices Report the average price at which each commodity was purchased in a balance report. .It Fl \-balance-format Ar FMT Specify the format to use for the .Ic balance report. .It Fl \-base Reduce convertible commodities down the bottom of the conversion, e.g. display time in seconds. .It Fl \-basis Pq Fl B Report the cost basis on all posting. Alias for .Fl \-cost .It Fl \-begin Ar DATE Pq Fl b Specify the start .Ar DATE of all calculations. Transactions before that date will be ignored. .It Fl \-bold-if Ar EXPR Print the entire line in bold if the given value expression is true. .It Fl \-budget Only display budgeted items. In a .Ic register report this displays transaction in the budget, in a balance report this displays accounts in the budget. .It Fl \-budget-format Ar FMT Specify the format to use for the .Ic budget report. .It Fl \-by-payee Pq Fl P Group postings in the register report by common payee names. .It Fl \-check-payees Enable strict and pedantic checking for payees as well as accounts, commodities and tags. .It Fl \-cleared Pq Fl C Display only cleared postings. .It Fl \-cleared-format Ar FMT Specify the format to use for the .Ic cleared report .It Fl \-collapse Pq Fl n Print only the top level accounts. .It Fl \-collapse-if-zero Collapse the account display only if it has a zero balance. .It Fl \-color Use color if the terminal supports it. Alias for .Fl \-ansi .It Fl \-columns Ar INT Make the .Ic register report .Ar INT characters wide. By default .Nm will use all available columns in your terminal. .It Fl \-cost Report the cost basis on all posting. Alias for .Fl \-basis . .It Fl \-count Direct .Nm to report the number of items when appended to the .Ic commodities , .Ic accounts or .Ic payees commands. .It Fl \-csv-format Ar FMT Format .Ic csv report according to .Ar FMT . .It Fl \-current Pq Fl c Shorthand for .Fl \-limit Ar "'date <= today'" . .It Fl \-daily Pq Fl D Shorthand for .Fl \-period Ar daily . .It Fl \-date Ar EXPR Transform the date of the transaction using .Ar EXPR . .It Fl \-date-format Ar DATEFMT Pq Fl y Print dates using .Ar DATEFMT . Refer to .Xr strftime 3 for details on the format string syntax. .It Fl \-datetime-format Ar DATETIMEFMT Print datetimes using .Ar DATETIMEFMT . Refer to .Xr strftime 3 for details on the format string syntax. .It Fl \-date-width Ar INT Specify the width, in characters, of the date column in the .Ic register report. .It Fl \-day-break Break up .Ic register report of timelog entries that span multiple days by day. .It Fl \-days-of-week Group transactions by the days of the week. Alias for .Fl \-dow . .It Fl \-dc Display register or balance in debit/credit format If you use .Fl \-dc with either the .Ic register or .Ic balance commands, you will now get separate columns for debits and credits. .It Fl \-debug Ar STR If .Nm has been built with debug options this will provide extra data during the run. .It Fl \-decimal-comma Direct .Nm to parse journals using the European standard comma as decimal separator, vice a period. .It Fl \-depth Ar INT Limit the depth of displayed accounts in balance and register reports. Any accounts of greater depth are folded into their parent at the specified level. For example with .Fl \-depth Ar 2 the account .Sy Expenses:Entertainment:Dining would be folded into .Sy Expenses:Entertainment for display. Importantly, this is a display predicate, which means it only affects display, not the total calculations. .It Fl \-detail Related to .Ic convert command. Synonym to .Fl \-rich-data option. .It Fl \-deviation Report each posting's deviation from the average. It is only meaningful in the .Ic register No and Ic prices reports. .It Fl \-display Ar EXPR Pq Fl d Display lines that satisfy the expression .Ar EXPR . .It Fl \-display-amount Ar EXPR Apply a transformation to the .Em displayed amount. This occurs after calculations occur. .It Fl \-display-total Ar EXPR Apply a transformation to the .Em displayed total. This occurs after calculations occur. .It Fl \-dow Group transactions by the days of the week. Alias for .Fl \-days-of-week . .It Fl \-download Cause quotes to be automagically downloaded, as needed, by running a script named .Em getquote and expecting that script to return a value understood by .Nm . A sample implementation of a .Em getquote script, implemented in Perl, is provided in the distribution. Downloaded quote price are then appended to the price database, usually specified using the environment variable .Ev LEDGER_PRICE_DB . .It Fl \-effective Show auxiliary dates for all calculations. Alias for .Fl \-aux-date . .It Fl \-empty Pq Fl E Include empty accounts in report. .It Fl \-end Ar DATE Pq Fl e Constrain the report so that transactions on or after .Ar DATE are not considered. .It Fl \-equity Related to the .Ic equity command. Gives current account balances in the form of a register report. .It Fl \-exact Report beginning and ending of periods by the date of the first and last posting occurring in that period. .It Fl \-exchange Qo Ar COMMODITY Oo , Ar COMMODITY, ... Oc Qc Pq Fl X Display values in terms of the given .Ar COMMODITY . If multiple commodities are given, values in a listed commodity will remain as-is, and others will be displayed in the first listed commodity they can be converted to. .It Fl \-file Ar FILE Pq Fl f Read journal data from .Ar FILE . .It Fl \-first Ar INT Print the first .Ar INT entries. Opposite of .Fl \-last Ar INT . Alias for .Fl \-head . .It Fl \-flat Force the full names of accounts to be used in the balance report. The balance report will not use an indented tree. .It Fl \-force-color Output TTY color codes even if the TTY doesn't support them. Useful for TTYs that don't advertise their capabilities correctly. .It Fl \-force-pager Force .Nm to paginate its output. .It Fl \-forecast-while Ar EXPR Continue forecasting while .Ar VEXPR is true. Alias for .Fl \-forecast . .It Fl \-forecast-years Ar INT Forecast at most .Ar INT years into the future. .It Fl \-format Ar FMT Pq Fl F Use the given format string .Ar FMT to print output. .It Fl \-gain Pq Fl G Report net gain or loss for commodities that have a price history. .It Fl \-generated Include auto-generated postings (such as those from automated transactions) in the report, in cases where you normally wouldn't want them. .It Fl \-group-by Ar EXPR Group transaction together in the .Ic register report. .Ar EXPR can be anything, although most common would be .Ar payee or .Ar commodity . The .Fn tag function is also useful here. .It Fl \-group-title-format Ar FMT Set the format for the headers that separate reports section of a grouped report. Only has effect with a .Fl \-group-by Ar EXPR register report. .It Fl \-head Ar INT Print the first .Ar INT entries. Opposite of .Fl \-tail Ar INT . Alias for .Fl \-first .It Fl \-help Print this man page. .It Fl \-immediate Evaluate calculations immediately rather than lazily. .It Fl \-import Ar FILE Import .Ar FILE as Python module. .It Fl \-init-file Ar FILE Pq Fl i Read .Ar FILE before any other .Nm file. This file may not contain any postings, but it may contain option settings. To specify options in the init file, use the same syntax as the command-line, but put each option on its own line. .It Fl \-inject Ar STR Use .Ar STR amounts in calculations. In case you know what amount a transaction should be, but the actual transaction has the wrong value you can use metadata .Ar STR to specify the expected amount. .It Fl \-input-date-format Ar DATEFMT Specify the input date format for journal entries. .It Fl \-invert Change the sign of all reported values. .It Fl \-last Ar INT . Report only the last .Ar INT entries. Opposite of .Fl \-first Ar INT . Only useful on a register report. Alias for .Fl \-tail . .It Fl \-leeway Ar INT Pq Fl Z Alias for .Fl \-price-expr . .It Fl \-limit Ar EXPR Pq Fl l Limit postings in calculations. .It Fl \-lot-dates Report the date on which each commodity in a balance report was purchased. .It Fl \-lot-notes Report the tag attached to each commodity in a balance report. .It Fl \-lot-prices Report the price at which each commodity in a balance report was purchased. .It Fl \-lots Report the date and price at which each commodity was purchased in a balance report. .It Fl \-lots-actual Preserve the uniqueness of commodities so they aren't merged during reporting without printing the lot annotations. .It Fl \-market Pq Fl V Use the latest market value for all commodities. .It Fl \-master-account Ar STR Prepend all account names with .Ar STR .It Fl \-meta Ar STR In the register report, prepend the transaction with the value of the given tag .Ar STR . .It Fl \-meta-width Ar INT Specify the width of the Meta column used for the .Fl \-meta Ar TAG options. .It Fl \-monthly Pq Fl M Shorthand for .Fl \-period Ar monthly . .It Fl \-no-aliases Aliases are completely ignored. .It Fl \-no-color Suppress any color TTY output. .It Fl \-no-pager Disables the pager on TTY output. .It Fl \-no-revalued Stop .Nm from showing <Revalued> postings. .It Fl \-no-rounding Don't output .Qq Li <Adjustment> postings. Note that this will cause the running total to often not add up! Its main use is for .Fl \-amount-data Pq Fl j and .Fl \-total-data Pq Fl J reports. .It Fl \-no-titles Suppress the output of group titles. .It Fl \-no-total Suppress printing the final total line in a balance report. .It Fl \-now Ar DATE Use .Ar DATE as the current date. This affects the output when using .Fl \-period , .Fl \-begin , .Fl \-end , or .Fl \-current to decide which dates lie in the past or future. .It Fl \-only Ar EXPR This is a postings predicate that applies after certain transforms have been executed, such as periodic gathering. .It Fl \-options Display the options in effect for this .Nm invocation, along with their values and the source of those values. .It Fl \-output Ar FILE Pq Fl o Redirect the output of .Nm to .Ar FILE . .It Fl \-pager Ar STR Use .Ar STR as the pager program. .It Fl \-payee Sets a value expression for formatting the payee. In the .Ic register report this prevents the second entry from having a date and payee for each transaction. .It Fl \-payee-width Ar INT Set the number of columns dedicated to the payee in the register report to .Ar INT . .It Fl \-pedantic Accounts, tags or commodities not previously declared will cause errors. .It Fl \-pending Use only postings that are marked pending. .It Fl \-percent Pq Fl % Calculate the percentage value of each account in a balance reports. Only works for account that have a single commodity. .It Fl \-period Ar PERIOD Pq Fl p Define a period expression that sets the time period during which transactions are to be accounted. For a .Ic register report only the transactions that satisfy the period expression with be displayed. For a balance report only those transactions will be accounted in the final balances. .It Fl \-period-sort Sort the posting within transactions using the given value expression. .It Fl \-permissive Quiet balance assertions. .It Fl \-pivot Ar TAG Produce a balance pivot report .Qq around the given .Ar TAG . .It Fl \-plot-amount-format Ar FMT Define the output format for an amount data plot. .It Fl \-plot-total-format Ar FMT Define the output format for a total data plot. .It Fl \-prepend-format Ar FMT Prepend .Ar FMT to every line of the output. .It Fl \-prepend-width Ar INT Reserve .Ar INT spaces at the beginning of each line of the output. .It Fl \-price Pq Fl I Use the price of the commodity purchase for performing calculations. .It Fl \-price-db Ar FILE .It Fl \-price-exp Ar STR Pq Fl Z Set the expected freshness of price quotes, in .Ar INT minutes. That is, if the last known quote for any commodity is older than this value, and if .Fl \-download is being used, then the Internet will be consulted again for a newer price. Otherwise, the old price is still considered to be fresh enough. Alias for .Fl \-leeway . .It Fl \-prices-format Ar FMT Set the format for the .Ic prices report. .It Fl \-pricedb-format Ar FMT Set the format expected for the historical price file. .It Fl \-primary-date Show primary dates for all calculations. Alias for .Fl \-actual-dates .It Fl \-quantity Pq Fl O Report commodity totals (this is the default). .It Fl \-quarterly Shorthand for .Fl \-period Ar quarterly . .It Fl \-raw In the .Ic print report, show transactions using the exact same syntax as specified by the user in their data file. Don't do any massaging or interpreting. Can be useful for minor cleanups, like just aligning amounts. .It Fl \-real Pq Fl R Account using only real transactions ignoring virtual and automatic transactions. .It Fl \-recursive-aliases Causes .Nm to try to expand aliases recursively, i.e. try to expand the result of an earlier expansion again, until no more expansions apply. .It Fl \-register-format Ar FMT Define the output format for the .Ic register report. .It Fl \-related Pq Fl r In a register report show the related account. This is the other .Em side of the transaction. .It Fl \-related-all Show all postings in a transaction, similar to .Fl \-related but show both sides of each transaction. .It Fl \-revalued Report discrepancy in values for manual reports by inserting <Revalued> postings. This is implied when using the .Fl \-exchange Pq Fl X or .Fl \-market Pq Fl V option. .It Fl \-revalued-only Show only <Revalued> postings. .It Fl \-revalued-total Display the sum of the revalued postings as the running total, which serves to show unrealized capital in a gain/losses report. .It Fl \-rich-data When generating a ledger transaction from a CSV file using the .Ic convert command, add CSV, Imported, and UUID meta-data. .It Fl \-seed Ar INT Set the random seed to .Ar INT for the .Ic generate command. Used as part of development testing. .It Fl \-script Ar FILE Execute a .Nm script. .It Fl \-sort Ar EXPR Pq Fl S Sort the register report based on the value expression .Ar EXPR . .\".It Fl \-sort-all Ar EXPR .It Fl \-sort-xacts Sort the posting within transactions using the given value expression. .It Fl \-start-of-week Ar STR Use .Ar STR as the particular day of the week to start when using the .Fl \-weekly option. .Ar STR can be day names, their abbreviations like .Qq Mon , or the weekday number starting at 0 for Sunday. .It Fl \-strict Accounts, tags or commodities not previously declared will cause warnings. .It Fl \-subtotal Pq Fl s Report register as a single subtotal. .It Fl \-tail Ar INT Report only the last .Ar INT entries. Only useful on a register report. Alias for .Fl \-last Ar INT .It Fl \-time-colon Display the value for commodities based on seconds as hours and minutes. Thus 8100s will be displayed as 2:15h instead of 2.25h. .It Fl \-time-report Add two columns to the .Ic balance report to show the earliest checkin and checkout times for timelog entries. .It Fl \-total Ar EXPR Pq Fl T Define a value expression used to calculate the total in reports. .It Fl \-total-data Pq Fl J Show only dates and totals to format the output for plots. .It Fl \-total-width Ar INT Set the width of the total field in the register report. .It Fl \-trace Ar INT Enable tracing. The .Ar INT specifies the level of trace desired. .It Fl \-truncate Ar STR Indicates how truncation should happen when the contents of columns exceed their width. Valid arguments for .Ar STR are .Ar leading , .Ar middle , and .Ar trailing . The default is smarter than any of these three, as it considers sub-names within the account name (that style is called .Qq abbreviate ) . .It Fl \-unbudgeted Show only un-budgeted postings. .It Fl \-uncleared Pq Fl U Use only uncleared transactions in calculations and reports. .It Fl \-unrealized Show generated unrealized gain and loss accounts in the balance report. .It Fl \-unrealized-gains Allow the user to specify what account name should be used for unrealized gains. Defaults to .Sy "Equity:Unrealized Gains" . Often set in one's init file to change the default. .It Fl \-unrealized-losses Allow the user to specify what account name should be used for unrealized losses. Defaults to .Sy "Equity:Unrealized Losses" . Often set in one's init file to change the default. .It Fl \-unround Perform all calculations without rounding and display results to full precision. .It Fl \-values Show the values used by each tag when used in combination with the .Ic tags command. .It Fl \-value-expr Ar EXPR Set a global value expression annotation. .It Fl \-verbose Print detailed information on the execution of .Nm . .It Fl \-verify Enable additional assertions during run-time. This causes a significant slowdown. When combined with .Fl \-debug Ar CODE .Nm will produce memory trace information. .It Fl \-verify-memory Verify that every constructed object is properly destructed. This is for debugging purposes only. .It Fl \-version Print version information and exit. .It Fl \-weekly Pq Fl W Shorthand for .Fl \-period Ar weekly . .It Fl \-wide Pq Fl w Assume 132 columns instead of the TTY width. .It Fl \-yearly Pq Fl Y Shorthand for .Fl \-period Ar yearly . .El .Sh PRE-COMMANDS Pre-commands are useful when you aren't sure how a command or option will work. The difference between a pre-command and a regular command is that pre-commands ignore the journal data file completely, nor is the user's init file read. .Bl -tag -width -indent .It Ic args No / Ic query Evaluate the given arguments and report how .Nm interprets it against the following model transaction: .Bd -literal -offset indent 2004/05/27 Book Store ; This note applies to all postings. :SecondTag: Expenses:Books 20 BOOK @ $10 ; Metadata: Some Value ; Typed:: $100 + $200 ; :ExampleTag: ; Here follows a note describing the posting. Liabilities:MasterCard $-200.00 .Ed .It Ic eval Evaluate the given value expression against the model transaction. .It Ic format Print details of how .Nm uses the given formatting description and apply it against a model transaction. .It Ic parse No / Ic expr Print details of how .Nm uses the given value expression description and apply it against a model transaction. .It Ic generate Randomly generates syntactically valid .Nm data from a seed. Used by the GenerateTests harness for development testing. .It Ic period Evaluate the given period and report how .Nm interprets it. .\".It Ic script .It Ic template Shows the insertion template that the .Ic xact command generates. This is a debugging command. .El .Sh QUERIES The syntax for reporting queries can get somewhat complex. It is a series of query terms with an implicit OR operator between them. The following terms are accepted: .Bl -tag -width "term and term" .It Ar regex A bare string is taken as a regular expression (PCRE) matching the full account name. Thus, to report the current balance for all assets and liabilities, you would use: .Pp .Dl ledger bal asset liab .It Ic payee Ar regex Pq Ic \&@ Ns Ar regex Query on the payee, rather than the account. .It Ic tag Ar regex Pq Ic \&% Ns Ar regex Query on tags. .It Ic note Ar regex Pq Ic \&= Ns Ar regex Query on anything found in an item's note. .It Ic code Ar regex Pq Ic \&# Ns Ar regex Query on the xact's optional code (which can be any string the user wishes). .It Ar term Cm and Ar term Query terms are joined by an implicit OR operator. You can change this to AND by using the .Cm and keyword. For example, to show food expenditures occurring at Shakee's Pizza, you could say: .Pp .Dl Li ledger reg food and @Shakee .It Ar term Cm or Ar term When you wish to be more explicit, use the OR operator. .It Ic show .It Cm not Ar term Reverse the logical meaning of the following term. This can be used with parentheses to great effect: .Pp .Dl Li ledger reg food and @Shakee and not dining .It \&( Ar term No \&) If you wish to mix OR and AND operators, it is often helpful to surround logical units with parentheses. \fBNOTE\fR: Because of the way some shells interpret parentheses, you should always escape them: .Pp .Dl Li ledger bal \e( assets or liab \e) and not food .El .Sh EXPRESSIONS .Bl -tag -width "partial_account" .It Fn abs value Return the absolute value of the given .Ar value . .It Sy account Return the posting's account. .It Sy account_base Return the base account, i.e. everything after the last account delimiter ':'. .\".It Sy account_amount .It Sy actual .\" Is there a difference between real and actual? Return true if the transaction is real, i.e not an automated or virtual transaction, false otherwise. .It Sy amount Return the amount of the posting. .It Sy amount_expr Return the calculated amount of the posting according to the .Fl \-amount option. .It Fn ansify_if value color bool Render the given .Ar value as a string, applying the proper ANSI escape codes to display it in the given .Ar color if .Ar bool is true. It typically checks the value of the option .Fl \-color , for example: .Dl Li ansify_if(amount, "blue", options.color) .It Sy beg_line Line number where entry for posting begins. .It Sy beg_pos Character position where entry for posting begins. .\".It Sy calculated .It Fn ceiling value Return the next integer of .Ar value toward +infinity. .It Sy cleared Return true if the posting was cleared, false otherwise. .It Sy code Return the transaction code, the string between the parenthesis after the date. .\".It Sy comment .It Fn commodity value Return the commodity of .Ar value or the posting amount when .Ar value was not specified. .\".It Sy cost .\".It Sy count .It Sy date Return the date of the posting. .\".It Sy depth .\".It Sy depth_spacer .\".It Sy display_amount .\".It Sy display_total .It Sy end_line Line number where entry for posting ends. .It Sy end_pos Character position where entry for posting ends. .It Fn floor value Return the next integer of .Ar value toward -infinity. .It Sy filename The name of the .Nm data file from whence the posting came. .It Fn format string Evaluate .Ar string as format just like the .Fl \-format option. .It Fn format_date date format Return the .Ar date as a string using .Ar format . Refer to .Xr strftime 3 for format string details. .It Fn format_datetime datetime format Return the .Ar datetime as a string using .Ar format . Refer to .Xr strftime 3 for format string details. .It Fn get_at seq index Return value at .Ar index from .Ar seq . Used internally to construct different reports. .It Fn has_meta tag Return true if the posting has metadata named .Ar tag , false otherwise. .It Fn has_tag tag Return true if the posting has metadata named .Ar tag , false otherwise. .It Fn is_seq value Return true if .Ar value is a sequence. Used internally. .It Fn join value Replace all newlines in .Ar value with .Li \en . .It Fn justify value first_width latter_width right_justify colorize Right or left justify the string representing .Ar value . The width of the field in the first line is given by .Ar first_width . For subsequent lines the width is given by .Ar latter_width . If .Ar latter_width is -1, .Ar first_width is used for all lines. If .Ar right_justify is true then the field is right justified within the width of the field. If it is false, then the field is left justified and padded to the full width of the field. If .Ar colorize is true, then ledger will honor color settings. .It Fn market value datetime Return the price of .Ar value at .Ar datetime . Note that .Ar datetime must be surrounded by brackets in order to be parsed correctly, e.g. .Bq 2012/03/23 . .It Fn meta name Return the value of metadata named .Ar name . .It Sy note Return the note for the posting. .It Sy now Return the current datetime. .\".It Sy null .It Sy options A variable that allows access to the values of the given command-line options using the long option names, e.g. to see whether .Fl \-daily Pq Fl D was given use .Sy option.daily . .\" .It Sy partial_account .It Sy payee Return the payee of the posting. .It Fn percent value_a value_b Return the percentage of .Ar value_a in relation to .Ar value_b (used as 100%). .It Sy pending Return true if the posting is marked as pending, false otherwise. .It Fn percent value_a value_b Return the percentage of .Ar value_a in relation to .Ar value_b . .\".It Sy post .\" A variable scope .It Fn print value Print .Ar value to stdout. Used internally for debugging. .It Fn quantity value Return the quantity of .Ar value for values that have a per-unit cost. .It Fn quoted expression Surround .Ar expression with double quotes. .It Fn quoted_rfc expression Surround .Ar expression with double quotes, compliant with rfc 4180. .It Sy real .\" Is there a difference between real and actual? Return true if the transaction is real, i.e not an automated or virtual transaction, false otherwise. .\".It Sy rounded .It Fn roundto value n Return .Ar value rounded to .Ar n digits. Does not affect formatting. .It Sy should_bold Return true if expression given to .Fl \-bold-if evaluates to true. Internal use only! .It Fn scrub value Clean .Ar value using various transformations such as round, stripping value annotations, and more. .\".It Sy status .It Fn strip value Strip value annotation from .Ar value . .\".It Sy subcount .It Fn tag name Return the value of tag named .Ar name . .It Fn to_amount value Convert .Ar value to an amount. Internal use only! .It Fn to_balance value Convert .Ar value to a balance. Internal use only! .It Fn to_boolean value Convert .Ar value to a boolean. Internal use only! .It Fn to_date value Convert .Ar value to a date. Internal use only! .It Fn to_datetime value Convert .Ar value to a datetime. Internal use only! .It Fn to_int value Return the integer value for .Ar value . .It Fn to_mask value Convert .Ar value to a mask. Internal use only! .It Fn to_sequence value Convert .Ar value to a sequence. Internal use only! .It Fn to_string value Convert .Ar value to a character string. .It Sy today Return today's date. .It Sy total Return the total of the posting. .It Sy total_expr Return the calculated total of the posting according to the .Fl \-total option. .It Fn trim value Trim leading and trailing whitespace from .Ar value . .It Fn truncated string total_len account_len Truncate .Ar string to .Ar total_len ensuring that each account is at least .Ar account_len long. .\".It Sy uncleared .It Sy virtual Return true if the transaction is virtual, e.g automated, false otherwise. .\".It Sy xact .\" A variable scope .El .\".Sh ENTRIES .\".Sh FORMATS .Sh DEBUG COMMANDS In addition to the regular reporting commands, .Nm also accepts several debug commands: .Bl -tag -width balance .It Ic args Oo Ar report-query Oc Display complete analysis of how .Nm interpreted the given .Ar report-query . Useful if you want to understand how report queries are translated into value expressions. .It Ic eval Oo Ar value-expression Oc Evaluate the given .Ar value-expression and prints the result. For more on value expressions, see the section .Sx EXPRESSIONS . .It Ic format Oo Ar format-string Oc Display an analysis of how .Ar format-string was parsed, and what it would look like applied to a sample transaction. For more on format strings, see the section .Sx FORMATS . .It Ic generate Generate 50 randomly composed yet valid .Nm transactions. .It Ic parse Oo Ar value-expression Oc Parse the given .Ar value-expression and display an analysis of the expression tree and its evaluated value. For more on value expressions, see the section .Sx EXPRESSIONS . .It Ic python Oo Ar file Oc Invoke a Python interpreter to read the given .Ar file . What is special about this is that the .Nm module is builtin, not read from disk, so it doesn't require .Nm to be installed anywhere, or the shared library variants to be built. .It Ic reload Reload all data files for the current session immediately. Can only be used in the .Tn REPL . .It Ic template Oo Ar draft-template Oc Display information about how .Ar draft-template was parsed. See the section on .Sx DRAFTS . .El .Sh ENVIRONMENT Every option to .Nm may be set using an environment variable if the option has a long name. For example setting the environment variable .Ev LEDGER_DATE_FORMAT="%d.%m.%Y" will have the same effect as specifying .Fl \-date-format Ar '%d.%m.%Y' on the command-line. Options on the command-line always take precedence over environment variable settings, however. .Sh FILES .Bl -tag -width -indent .It Pa $XDG_CONFIG_HOME/ledger/ledgerrc .It Pa ~/.config/ledger/ledgerrc .It Pa ~/.ledgerrc Your personal .Nm initializations. .El .Sh SEE ALSO .Xr beancount 1 , .Xr hledger 1 The full documentation for .Nm is maintained as a Texinfo manual. If the .Nm info program is installed on your system, the command .Dl info ledger3 should give you access to the complete manual. .Sh AUTHORS .An "John Wiegley" .Aq johnw@newartisans.com .\" .Sh BUGS \" Document known, unremedied bugs .\" .Sh HISTORY \" Document history if command behaves in a unique manner ���������������������������������������������������������������������������������ledger-3.3.2/doc/ledger3.texi�����������������������������������������������������������������������0000664�0000000�0000000�00001270163�14411236400�0015753�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������\input texinfo @c -*-texinfo-*- @setfilename ledger3.info @include version.texi @set FIXME:UNDOCUMENTED @sc{undocumented}! Please help by contributing documentation for this feature. @set InternalUseOnly For internal use only. @settitle Ledger: Command-Line Accounting @codequoteundirected on @c Before release, run C-u C-c C-u C-a (texinfo-all-menus-update with @c a prefix arg). This updates the node pointers, which texinfmt.el @c needs. @c | Formatting | Indexing | | @c | | @cindex | concept | @c | @command | @findex | Ledger CLI Command (like balance) | @c | @option | @findex | Ledger CLI Option (like --market) | @c | @var | | Ledger CLI option Variable (like -f FILE) | @c | | | Ledger file Syntax | @c | @samp | | Valued example or single char | @c | @file | | File, Buffer | @c | @file | | Program (like ledger, report, acprep) | @c Restructuring manual ideas @c http://beyondgrep.com/documentation/ack-2.04-man.html @c How to make documented ledger examples validate automatically. @c @c The test/DocTests.py script will be run along with the other tests @c when using ctest or acprep check. @c The script parses the texinfo file and looks for three kinds of @c specially marked @smallexamples, then it will run the ledger @c command from the example, and compare the results with the output @c from the documentation. @c @c To specially mark a @smallexample append @c command:UUID, where @c UUID is the first 7 digits from the commands sha1sum, e.g.: @c @c @smallexample @c command:CDE330A @c $ ledger -f sample.dat reg expenses @c @end smallexample @c @c Then DocTests.py will look for corresponding documented output, @c which may appear anywhere in the file, and is marked with @c @smallexample @c output:UUID where UUID is the UUID from the @c corresponding ledger command example, e.g.: @c @c @smallexample @c output:CDE330A @c 04-May-27 Book Store Expenses:Books $20.00 $20.00 @c Expenses:Cards $40.00 $60.00 @c Expenses:Docs $30.00 $90.0 @c @end smallexample @c @c Now where does this data in sample.dat come from? @c DocTests.py is a bit smart about ledger's file argument, since @c it will check if the given filename exists in the test/input/ @c directory. @c @c Sometimes the journal data for an example is specified within @c the documentation itself, in that case the journal example data @c needs to be specially marked as well using @smallexample @c input:UUID, @c again with the UUID being the UUID of the corresponding ledger example @c command. If multiple inputs with the same UUID are found they will be @c concatenated together and given as one set of data to the example command. @c @c @smallexample @c input:35CB2A3 @c 2014/02/09 The Italian Place @c Expenses:Food:Dining $ 36.84 @c Assets:Cash @c @end smallexample @c @c @smallexample @c command:35CB2A3 @c $ ledger -f inline.dat accounts @c @end smallexample @c @c @smallexample @c output:35CB2A3 @c Assets:Cash @c Expenses:Food:Dining @c @end smallexample @c @c To use different example commands with the same input from the documentation @c add with_input:UUID to the example command, where UUID is the UUID of the input, @c e.g.: @c @c @smallexample @c command:94FD2B6,with_input:35CB2A3 @c $ ledger -f inline.dat bal expenses @c @end smallexample @c @c @smallexample @c output:94FD2B6 @c $ 36.84 Expenses:Food:Dining @c @end smallexample @c @c To pass additional input to ledger for certain commands, e.g. convert add @c with_file:filename to the example command and add a file:UUID to an example @c that holds the additional input, where UUID is the UUID of the command, @c e.g.: @c @c @smallexample @c file:download.csv @c 767718,12/13/2011,"Withdrawal","ACE HARDWARE 16335 S HOUGHTON RD",-8.80,,00001640.04,, @c @end smallexample @c @c @smallexample @c command:94FD2B6,with_file:download.csv @c $ ledger -f sample.dat convert download.csv @c @end smallexample @c @c Additionally DocTests.py will pass --args-only and --columns 80 to ledger @c to ignore any default arguments from the environment or .ledgerrc. @c @c To manually run the tests in this file run: @c $ ./test/DocTests.py -vv --ledger ./ledger --file ./doc/ledger3.texi @copying Copyright @copyright{} 2003--2023, John Wiegley. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @itemize @item Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. @item Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. @item Neither the name of New Artisans LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. @end itemize THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @end copying @dircategory User Applications @direntry * Ledger3: (ledger3). Command-Line Accounting @end direntry @documentencoding UTF-8 @iftex @finalout @end iftex @titlepage @title Ledger: Command-Line Accounting @subtitle For Version @value{VERSION} of Ledger @author John Wiegley @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top, Introduction to Ledger, (dir), (dir) @top Overview Ledger is a command-line accounting tool that provides double-entry accounting based on a text journal. It provides no bells or whistles, and returns the user to the days before user interfaces were even a twinkling in their fathers' CRTs. @end ifnottex @menu * Introduction to Ledger:: * Ledger Tutorial:: * Principles of Accounting with Ledger:: * Keeping a Journal:: * Transactions:: * Building Reports:: * Reporting Commands:: * Command-Line Syntax:: * Budgeting and Forecasting:: * Time Keeping:: * Value Expressions:: * Format Strings:: * Extending with Python:: * Ledger for Developers:: * Major Changes from version 2.6:: * Example Journal File:: * Miscellaneous Notes:: * Concepts Index:: * Commands & Options Index:: @end menu @node Introduction to Ledger, Ledger Tutorial, Top, Top @chapter Introduction to Ledger @menu * Fat-free Accounting:: * Building the program:: * Getting help:: * Third-Party Ledger Tutorials:: @end menu @node Fat-free Accounting, Building the program, Introduction to Ledger, Introduction to Ledger @section Fat-free Accounting Ledger is an accounting tool with the moxie to exist. It provides no bells or whistles, and returns the user to the days before user interfaces were even a twinkling in their father's CRT. What it does offer is a double-entry accounting journal with all the flexibility and muscle of its modern day cousins, without any of the fat. Think of it as the Bran Muffin of accounting tools. To use it, you need to start keeping a journal. This is the basis of all accounting, and if you haven't started yet, now is the time to learn. The little booklet that comes with your checkbook is a journal, so we'll describe double-entry accounting in terms of that. @c If you use another GUI accounting program like GnuCash, the vast @c majority of its functionality is geared towards helping you keep @c a journal. A checkbook journal records debits (subtractions, or withdrawals) and credits (additions, or deposits) with reference to a single account: the checking account. Where the money comes from, and where it goes to, are described in the payee field, where you write the person or company's name. The ultimate aim of keeping a checkbook journal is to know how much money is available to spend. That's really the aim of all journals. @cindex postings What computers add is the ability to walk through these postings, and tell you things about your spending habits; to let you devise budgets and get control over your spending; to squirrel away money into virtual savings account without having to physically move money around; etc. As you keep your journal, you are recording information about your life and habits, and sometimes that information can start telling you things you aren't aware of. Such is the aim of all good accounting tools. The next step up from a checkbook journal, is a journal that keeps track of all your accounts, not just checking. In such a journal, you record not only who gets paid---in the case of a debit---but where the money came from. In a checkbook journal, it's assumed that all the money comes from your checking account. But in a general journal, you write postings in two lines: the source account and target account. @emph{There must always be a debit from at least one account for every credit made to another account}. This is what is meant by ``double-entry'' accounting: the journal must always balance to zero, with an equal number of debits and credits. For example, let's say you have a checking account and a brokerage account, and you can write checks from both of them. Rather than keep two checkbooks, you decide to use one journal for both. In this general journal you need to record a payment to Pacific Bell for your monthly phone bill, and a transfer (via check) from your brokerage account to your checking account. The Pacific Bell bill is $23.00, let's say, and you want to pay it from your checking account. In the general journal you need to say where the money came from, in addition to where it's going to. These transactions might look like this: @smallexample 9/29 Pacific Bell $23.00 $23.00 Checking $-23.00 0 9/30 Checking $100.00 $100.00 (123) Brokerage $-100.00 0 @end smallexample The posting must balance to $0: $23 went to Pacific Bell, $23 came from Checking. The next entry shows check number 123 written against your brokerage account, transferring money to your checking account. There is nothing left over to be accounted for, since the money has simply moved from one account to another in both cases. This is the basis of double-entry accounting: money never pops in or out of existence; it is always a posting from one account to another. Keeping a general journal is the same as keeping two separate journals: One for Pacific Bell and one for Checking. In that case, each time a payment is written into one, you write a corresponding withdrawal into the other. This makes it easier to write in a ``running balance'', since you don't have to look back at the last time the account was referenced---but it also means having a lot of journal books, if you deal with multiple accounts. @cindex account, meaning of @cindex meaning of account Here is a good place for an aside on the use of the word ``account''. Most private people consider an account to be something that holds money at an institution for them. Ledger uses a more general definition of the word. An account is anywhere money can go. Other finance programs use ``categories'', Ledger uses accounts. So, for example, if you buy some groceries at Trader Joe's, then more groceries at Whole Food Market, you might assign the transactions like this @smallexample @c input:validate 2011/03/15 Trader Joe's Expenses:Groceries $100.00 Assets:Checking 2011/03/15 Whole Food Market Expenses:Groceries $75.00 Assets:Checking @end smallexample In both cases the money goes to the @samp{Groceries} account, even though the payees were different. You can set up your accounts in any way you choose. Enter the beauty of computerized accounting. The purpose of the Ledger program is to make general journal accounting simple, by keeping track of the balances for you. Your only job is to enter the postings. If an individual posting does not balance, Ledger displays an error and indicates the incorrect posting.@footnote{In some special cases, it automatically balances this transaction for you.} In summary, there are two aspects of Ledger use: updating the journal data file, and using the Ledger tool to view the summarized result of your transactions. And just for the sake of example---as a starting point for those who want to dive in head-first---here are the journal transactions from above, formatted as the Ledger program wishes to see them: @smallexample @c input:48DDF26 2004/09/29 Pacific Bell Expenses:Pacific Bell $23.00 Assets:Checking @end smallexample The account balances and registers in this file, if saved as @file{ledger.dat}, could be reported using: @smallexample @c command:48DDF26 $ ledger -f ledger.dat balance @end smallexample @smallexample @c output:48DDF26 $-23.00 Assets:Checking $23.00 Expenses:Pacific Bell -------------------- 0 @end smallexample Or @smallexample @c command:8C7295F,with_input:48DDF26 $ ledger -f ledger.dat register checking @end smallexample @smallexample @c output:8C7295F 04-Sep-29 Pacific Bell Assets:Checking $-23.00 $-23.00 @end smallexample And even: @smallexample @c command:BB32EF2,with_input:48DDF26 $ ledger -f ledger.dat register Bell @end smallexample @smallexample @c output:BB32EF2 04-Sep-29 Pacific Bell Expenses:Pacific Bell $23.00 $23.00 @end smallexample An important difference between Ledger and other finance packages is that Ledger will never alter your input file. You can create and edit that file in any way you prefer, but Ledger is only for analyzing the data, not for altering it. @node Building the program, Getting help, Fat-free Accounting, Introduction to Ledger @section Building the program Ledger is written in ANSI C++, and should compile on any unix platform. The easiest way to build and install ledger is to use the prepared acprep script, that does a lot of the footwork: @smallexample # to install missing dependencies ./acprep dependencies # building ledger ./acprep update # to run the actual installation make install @end smallexample See the `help` subcommand to `acprep`, which explains some of its many options. You can run `make check` to confirm the result, and `make install` to install. If these instructions do not work for you, you can check the `INSTALL.md` in the source directory for more up to date build instructions. @node Getting help, Third-Party Ledger Tutorials, Building the program, Introduction to Ledger @section Getting help @findex help Ledger has a complete online help system based on GNU Info. This manual can be searched directly from the command-line using @code{info ledger}, which will bring up this entire manual in your TTY. Alternatively, the shorter man page can be accessed from the command-line either via @code{man ledger} or @code{ledger --help} If you need help on how to use Ledger, or run into problems, you can join the Ledger mailing list at @url{http://groups.google.com/group/ledger-cli}. You can also find help in the @code{#ledger} channel on the IRC server @code{irc.libera.chat}. @node Third-Party Ledger Tutorials, , Getting help, Introduction to Ledger @section Third-Party Ledger Tutorials There are plenty of people using Ledger for accounting applications. Some have documented how they use Ledger's features to solve their accounting problems. One such tutorial, specifically designed for non-profit charities that seek to use Ledger, can be found at @url{https://k.sfconservancy.org/NPO-Accounting/npo-ledger-cli} (with a copy on GitHub also available at @url{https://github.com/conservancy/npo-ledger-cli/}). If you're looking for information about how to use Ledger's tagging system to handle invoicing, track expenses by program targets, and other such concepts, you might find the tutorial useful. (Some of the auditor reporting scripts that relate to the aforementioned Ledger setup can be found @var{contrib/non-profit-audit-reports/} in Ledger's own source repository.) @node Ledger Tutorial, Principles of Accounting with Ledger, Introduction to Ledger, Top @chapter Ledger Tutorial @cindex tutorial @menu * Start a Journal File:: * Run a Few Reports:: @end menu @node Start a Journal File, Run a Few Reports, Ledger Tutorial, Ledger Tutorial @section Start a Journal File @cindex journals A journal is a record of your financial transactions and will be central to using Ledger. For now we just want to get a taste of what Ledger can do. An example journal is included with the source code distribution, called @file{drewr3.dat} (@pxref{Example Journal File}). Copy it someplace convenient and open up a terminal window in that directory. If you would rather start with your own journal right away please @pxref{Keeping a Journal}. @node Run a Few Reports, , Start a Journal File, Ledger Tutorial @section Run a Few Reports @menu * Balance Report:: * Register Report:: * Cleared Report:: * Using the Windows Command-Line:: @end menu Please note that as a command-line program, Ledger is controlled from your shell. There are several different command shells that all behave slightly differently with respect to some special characters. In particular, the ``bash'' shell will interpret @samp{$} signs differently than ledger and they must be escaped to reach the actual program. Another example is ``zsh'', which will interpret @samp{^} differently than ledger expects. In all cases that follow you should take that into account when entering the command-line arguments as given. There are too many variations between shells to give concrete examples for each. @node Balance Report, Register Report, Run a Few Reports, Run a Few Reports @subsection Balance Report @cindex balance report @findex balance To find the balances of all of your accounts, run this command: @smallexample @c command:1071890 $ ledger -f drewr3.dat balance @end smallexample Ledger will generate: @smallexample @c output:1071890 $ -3,804.00 Assets $ 1,396.00 Checking $ 30.00 Business $ -5,200.00 Savings $ -1,000.00 Equity:Opening Balances $ 6,654.00 Expenses $ 5,500.00 Auto $ 20.00 Books $ 300.00 Escrow $ 334.00 Food:Groceries $ 500.00 Interest:Mortgage $ -2,030.00 Income $ -2,000.00 Salary $ -30.00 Sales $ -63.60 Liabilities $ -20.00 MasterCard $ 200.00 Mortgage:Principal $ -243.60 Tithe -------------------- $ -243.60 @end smallexample @noindent Showing you the balance of all accounts. Options and search terms can pare this down to show only the accounts you want. A more useful report is to show only your Assets and Liabilities: @smallexample @c command:5BF4D8E $ ledger -f drewr3.dat balance Assets Liabilities @end smallexample @smallexample @c output:5BF4D8E $ -3,804.00 Assets $ 1,396.00 Checking $ 30.00 Business $ -5,200.00 Savings $ -63.60 Liabilities $ -20.00 MasterCard $ 200.00 Mortgage:Principal $ -243.60 Tithe -------------------- $ -3,867.60 @end smallexample @node Register Report, Cleared Report, Balance Report, Run a Few Reports @subsection Register Report @cindex register report @findex register To show all transactions and a running total: @smallexample @c command:66E3A2C $ ledger -f drewr3.dat register @end smallexample @noindent Ledger will generate: @smallexample @c output:66E3A2C 10-Dec-01 Checking balance Assets:Checking $ 1,000.00 $ 1,000.00 Equit:Opening Balances $ -1,000.00 0 10-Dec-20 Organic Co-op Expense:Food:Groceries $ 37.50 $ 37.50 Expense:Food:Groceries $ 37.50 $ 75.00 Expense:Food:Groceries $ 37.50 $ 112.50 Expense:Food:Groceries $ 37.50 $ 150.00 Expense:Food:Groceries $ 37.50 $ 187.50 Expense:Food:Groceries $ 37.50 $ 225.00 Assets:Checking $ -225.00 0 10-Dec-28 Acme Mortgage Lia:Mortgage:Principal $ 200.00 $ 200.00 Expe:Interest:Mortgage $ 500.00 $ 700.00 Expenses:Escrow $ 300.00 $ 1,000.00 Assets:Checking $ -1,000.00 0 11-Jan-02 Grocery Store Expense:Food:Groceries $ 65.00 $ 65.00 Assets:Checking $ -65.00 0 11-Jan-05 Employer Assets:Checking $ 2,000.00 $ 2,000.00 Income:Salary $ -2,000.00 0 (Liabilities:Tithe) $ -240.00 $ -240.00 11-Jan-14 Bank Assets:Savings $ 300.00 $ 60.00 Assets:Checking $ -300.00 $ -240.00 11-Jan-19 Grocery Store Expense:Food:Groceries $ 44.00 $ -196.00 Assets:Checking $ -44.00 $ -240.00 11-Jan-25 Bank Assets:Checking $ 5,500.00 $ 5,260.00 Assets:Savings $ -5,500.00 $ -240.00 11-Jan-25 Tom's Used Cars Expenses:Auto $ 5,500.00 $ 5,260.00 Assets:Checking $ -5,500.00 $ -240.00 11-Jan-27 Book Store Expenses:Books $ 20.00 $ -220.00 Liabilities:MasterCard $ -20.00 $ -240.00 11-Dec-01 Sale Asse:Checking:Business $ 30.00 $ -210.00 Income:Sales $ -30.00 $ -240.00 (Liabilities:Tithe) $ -3.60 $ -243.60 @end smallexample @noindent To limit this to a more useful subset, simply add the accounts you are interested in seeing transactions for: @cindex accounts, limiting by @cindex limiting by accounts @smallexample @c command:96B0EB3 $ ledger -f drewr3.dat register Groceries @end smallexample @smallexample @c output:96B0EB3 10-Dec-20 Organic Co-op Expense:Food:Groceries $ 37.50 $ 37.50 Expense:Food:Groceries $ 37.50 $ 75.00 Expense:Food:Groceries $ 37.50 $ 112.50 Expense:Food:Groceries $ 37.50 $ 150.00 Expense:Food:Groceries $ 37.50 $ 187.50 Expense:Food:Groceries $ 37.50 $ 225.00 11-Jan-02 Grocery Store Expense:Food:Groceries $ 65.00 $ 290.00 11-Jan-19 Grocery Store Expense:Food:Groceries $ 44.00 $ 334.00 @end smallexample @noindent Which matches the balance reported for the @samp{Groceries} account: @smallexample @c command:AECD64E $ ledger -f drewr3.dat balance Groceries @end smallexample @smallexample @c output:AECD64E $ 334.00 Expenses:Food:Groceries @end smallexample @noindent If you would like to find transaction to only a certain payee use @samp{payee} or @samp{@@}: @smallexample @c command:C6BC57E $ ledger -f drewr3.dat register payee "Organic" @end smallexample @smallexample @c output:C6BC57E 10-Dec-20 Organic Co-op Expense:Food:Groceries $ 37.50 $ 37.50 Expense:Food:Groceries $ 37.50 $ 75.00 Expense:Food:Groceries $ 37.50 $ 112.50 Expense:Food:Groceries $ 37.50 $ 150.00 Expense:Food:Groceries $ 37.50 $ 187.50 Expense:Food:Groceries $ 37.50 $ 225.00 Assets:Checking $ -225.00 0 @end smallexample @node Cleared Report, Using the Windows Command-Line, Register Report, Run a Few Reports @subsection Cleared Report @cindex cleared report @findex cleared A very useful report is to show what your obligations are versus what expenditures have actually been recorded. It can take several days for a check to clear, but you should treat it as money spent. The @command{cleared} report shows just that (note that the @command{cleared} report will not format correctly for accounts that contain multiple commodities): @smallexample @c command:B86F6A6 $ ledger -f drewr3.dat cleared @end smallexample @smallexample @c output:B86F6A6 $ -3,804.00 $ 775.00 Assets $ 1,396.00 $ 775.00 10-Dec-20 Checking $ 30.00 0 Business $ -5,200.00 0 Savings $ -1,000.00 $ -1,000.00 10-Dec-01 Equity:Opening Balances $ 6,654.00 $ 225.00 Expenses $ 5,500.00 0 Auto $ 20.00 0 Books $ 300.00 0 Escrow $ 334.00 $ 225.00 10-Dec-20 Food:Groceries $ 500.00 0 Interest:Mortgage $ -2,030.00 0 Income $ -2,000.00 0 Salary $ -30.00 0 Sales $ -63.60 0 Liabilities $ -20.00 0 MasterCard $ 200.00 0 Mortgage:Principal $ -243.60 0 Tithe ---------------- ---------------- --------- $ -243.60 0 @end smallexample @noindent The first column shows the outstanding balance, the second column shows the ``cleared'' balance. @node Using the Windows Command-Line, , Cleared Report, Run a Few Reports @subsection Using the Windows Command-Line @cindex windows cmd.exe @cindex currency symbol display on windows Using ledger under the windows command shell has one significant limitation. CMD.EXE is limited to standard ASCII characters and as such cannot display any currency symbols other than dollar signs @samp{$}. @node Principles of Accounting with Ledger, Keeping a Journal, Ledger Tutorial, Top @chapter Principles of Accounting with Ledger @menu * Accounting with Ledger:: * Stating where money goes:: * Assets and Liabilities:: * Commodities and Currencies:: * Accounts and Inventories:: * Understanding Equity:: * Dealing with Petty Cash:: * Working with multiple funds and accounts:: @end menu @node Accounting with Ledger, Stating where money goes, Principles of Accounting with Ledger, Principles of Accounting with Ledger @section Accounting with Ledger @cindex double-entry accounting Accounting is simply tracking your money. It can range from nothing, and just waiting for automatic overdraft protection to kick in, or not, to a full-blown double-entry accounting system. Ledger accomplishes the latter. With ledger you can handle your personal finances or your business's. Double-entry accounting scales. @node Stating where money goes, Assets and Liabilities, Accounting with Ledger, Principles of Accounting with Ledger @section Stating where money goes @cindex credits and debits Accountants will talk of ``credits'' and ``debits'', but the meaning is often different from the layman's understanding. To avoid confusion, Ledger uses only subtractions and additions, although the underlying intent is the same as standard accounting principles. Recall that every posting will involve two or more accounts. Money is transferred from one or more accounts to one or more other accounts. To record the posting, an amount is @emph{subtracted} from the source accounts, and @emph{added} to the target accounts. In order to write a Ledger transaction correctly, you must determine where the money comes from and where it goes to. For example, when you are paid a salary, you must add money to your bank account and also subtract it from an income account: @smallexample @c input:validate 9/29 My Employer Assets:Checking $500.00 Income:Salary $-500.00 @end smallexample @cindex income is negative @cindex why is income negative Why is the Income a negative figure? When you look at the balance totals for your ledger, you may be surprised to see that Expenses are a positive figure, and Income is a negative figure. It may take some getting used to, but to properly use a general ledger you must think in terms of how money moves. Rather than Ledger ``fixing'' the minus signs, let's understand why they are there. When you earn money, the money has to come from somewhere. Let's call that somewhere ``society''. In order for society to give you an income, you must take money away (withdraw) from society in order to put it into (make a payment to) your bank. When you then spend that money, it leaves your bank account (a withdrawal) and goes back to society (a payment). This is why Income will appear negative---it reflects the money you have drawn from society---and why Expenses will be positive---it is the amount you've given back. These additions and subtractions will always cancel each other out in the end, because you don't have the ability to create new money: it must always come from somewhere, and in the end must always leave. This is the beginning of economy, after which the explanation gets terribly difficult. Based on that explanation, here's another way to look at your balance report: every negative figure means that that account or person or place has less money now than when you started your ledger; and every positive figure means that that account or person or place has more money now than when you started your ledger. Make sense? @node Assets and Liabilities, Commodities and Currencies, Stating where money goes, Principles of Accounting with Ledger @section Assets and Liabilities @cindex assets and liabilities @cindex debts are liabilities Assets are money that you have, and Liabilities are money that you owe. ``Liabilities'' is just a more inclusive name for Debts. An Asset is typically increased by transferring money from an Income account, such as when you get paid. Here is a typical transaction: @smallexample @c input:6B43DD4 2004/09/29 My Employer Assets:Checking $500.00 Income:Salary @end smallexample Money, here, comes from an Income account belonging to @samp{My Employer}, and is transferred to your checking account. The money is now yours, which makes it an Asset. Liabilities track money owed to others. This can happen when you borrow money to buy something, or if you owe someone money. Here is an example of increasing a MasterCard liability by spending money with it: @smallexample @c input:6B43DD4 2004/09/30 Restaurant Expenses:Dining $25.00 Liabilities:MasterCard @end smallexample The Dining account balance now shows $25 spent on Dining, and a corresponding $25 owed on the MasterCard---and therefore shown as $-25.00. The MasterCard liability shows up as negative because it offsets the value of your assets. The combined total of your Assets and Liabilities is your net worth. So to see your current net worth, use this command: @smallexample @c command:6B43DD4 $ ledger balance ^assets ^liabilities @end smallexample @smallexample @c output:6B43DD4 $500.00 Assets:Checking $-25.00 Liabilities:MasterCard -------------------- $475.00 @end smallexample In a similar vein, your Income accounts show up negative, because they transfer money @emph{from} an account in order to increase your assets. Your Expenses show up positive because that is where the money went to. The combined total of Income and Expenses is your cash flow. A positive cash flow means you are spending more than you make, since income is always a negative figure. To see your current cash flow, use this command: @smallexample @c command:DB128F3,with_input:6B43DD4 $ ledger balance ^income ^expenses @end smallexample @smallexample @c output:DB128F3 $25.00 Expenses:Dining $-500.00 Income:Salary -------------------- $-475.00 @end smallexample Another common question to ask of your expenses is: How much do I spend each month on X? Ledger provides a simple way of displaying monthly totals for any account. Here is an example that summarizes your monthly automobile expenses: @smallexample @c command:DB524E4 $ ledger -M register -f drewr3.dat expenses:auto @end smallexample @smallexample @c output:DB524E4 11-Jan-01 - 11-Jan-31 Expenses:Auto $ 5,500.00 $ 5,500.00 @end smallexample This assumes, of course, that you use account names like @samp{Expenses:Auto:Gas} and @samp{Expenses:Auto:Repair}. @menu * Tracking reimbursable expenses:: @end menu @node Tracking reimbursable expenses, , Assets and Liabilities, Assets and Liabilities @subsection Tracking reimbursable expenses @cindex reimbursable expense tracking Sometimes you will want to spend money on behalf of someone else, which will eventually get repaid. Since the money is still @emph{yours}, it is really an asset. And since the expenditure was for someone else, you don't want it contaminating your Expenses reports. You will need to keep an account for tracking reimbursements. This is fairly easy to do in ledger. When spending the money, spend it @emph{to} your Assets:Reimbursements, using a different account for each person or business that you spend money for. For example: @smallexample @c input:validate 2004/09/29 Circuit City Assets:Reimbursements:Company XYZ $100.00 Liabilities:MasterCard @end smallexample This shows $100.00 spent on a MasterCard at Circuit City, with the expense was made on behalf of Company XYZ. Later, when Company XYZ pays the amount back, the money will transfer from that reimbursement account back to a regular asset account: @smallexample @c input:validate 2004/09/29 Company XYZ Assets:Checking $100.00 Assets:Reimbursements:Company XYZ @end smallexample This deposits the money owed from Company XYZ into a checking account, presumably because they paid the amount back with a check. But what to do if you run your own business, and you want to keep track of expenses made on your own behalf, while still tracking everything in a single ledger file? This is more complex, because you need to track two separate things: 1) The fact that the money should be reimbursed to you, and 2) What the expense account was, so that you can later determine where your company is spending its money. This kind of posting is best handled with mirrored postings in two different files, one for your personal accounts, and one for your company accounts. But keeping them in one file involves the same kinds of postings, so those are what is shown here. First, the personal transaction, which shows the need for reimbursement: @smallexample @c input:validate 2004/09/29 Circuit City Assets:Reimbursements:Company XYZ $100.00 Liabilities:MasterCard @end smallexample This is the same as above, except that you own Company XYZ, and are keeping track of its expenses in the same ledger file. This transaction should be immediately followed by an equivalent transaction, which shows the kind of expense, and also notes the fact that $100.00 is now payable to you: @smallexample @c input:validate 2004/09/29 Circuit City Company XYZ:Expenses:Computer:Software $100.00 Company XYZ:Accounts Payable:Your Name @end smallexample This second transaction shows that Company XYZ has just spent $100.00 on software, and that this $100.00 came from Your Name, which must be paid back. These two transactions can also be merged, to make things a little clearer. Note that all amounts must be specified now: @smallexample @c input:validate 2004/09/29 Circuit City Assets:Reimbursements:Company XYZ $100.00 Liabilities:MasterCard $-100.00 Company XYZ:Expenses:Computer:Software $100.00 Company XYZ:Accounts Payable:Your Name $-100.00 @end smallexample To ``pay back'' the reimbursement, just reverse the order of everything, except this time drawing the money from a company asset, paying it to accounts payable, and then drawing it again from the reimbursement account, and paying it to your personal asset account. It's easier shown than said: @smallexample @c input:validate 2004/10/15 Company XYZ Assets:Checking $100.00 Assets:Reimbursements:Company XYZ $-100.00 Company XYZ:Accounts Payable:Your Name $100.00 Company XYZ:Assets:Checking $-100.00 @end smallexample And now the reimbursements account is paid off, accounts payable is paid off, and $100.00 has been effectively transferred from the company's checking account to your personal checking account. The money simply ``waited''---in both @samp{Assets:Reimbursements:Company XYZ}, and @samp{Company XYZ:Accounts Payable:Your Name}---until such time as it could be paid off. The value of tracking expenses from both sides like that is that you do not contaminate your personal expense report with expenses made on behalf of others, while at the same time making it possible to generate accurate reports of your company's expenditures. It is more verbose than just paying for things with your personal assets, but it gives you a very accurate information trail. The advantage to keep these doubled transactions together is that they always stay in sync. The advantage to keeping them apart is that it clarifies the transfer's point of view. To keep the postings in separate files, just separate the two transactions that were joined above. For example, for both the expense and the pay-back shown above, the following four transactions would be created. Two in your personal ledger file: @smallexample @c input:990E0D1 2004/09/29 Circuit City Assets:Reimbursements:Company XYZ $100.00 Liabilities:MasterCard $-100.00 2004/10/15 Company XYZ Assets:Checking $100.00 Assets:Reimbursements:Company XYZ $-100.00 @end smallexample And two in your company ledger file: @smallexample @c input:990E0D1 apply account Company XYZ 2004/09/29 Circuit City Expenses:Computer:Software $100.00 Accounts Payable:Your Name $-100.00 2004/10/15 Company XYZ Accounts Payable:Your Name $100.00 Assets:Checking $-100.00 end apply account @end smallexample (Note: The @code{apply account} above means that all accounts mentioned in the file are children of that account. In this case it means that all activity in the file relates to Company XYZ). After creating these transactions, you will always know that $100.00 was spent using your MasterCard on behalf of Company XYZ, and that Company XYZ spent the money on computer software and paid it back about two weeks later. @smallexample @c command:990E0D1 $ ledger balance --no-total @end smallexample @smallexample @c output:990E0D1 $100.00 Assets:Checking 0 Company XYZ $-100.00 Assets:Checking $100.00 Expenses:Computer:Software $-100.00 Liabilities:MasterCard @end smallexample @node Commodities and Currencies, Accounts and Inventories, Assets and Liabilities, Principles of Accounting with Ledger @section Commodities and Currencies Ledger makes no assumptions about the commodities you use; it only requires that you specify a commodity. The commodity may be any non-numeric string that does not contain a period, comma, forward slash or at-sign. It may appear before or after the amount, although it is assumed that symbols appearing before the amount refer to currencies, while non-joined symbols appearing after the amount refer to commodities. Here are some valid currency and commodity specifiers: @smallexample $20.00 ; currency: twenty US dollars 40 AAPL ; commodity: 40 shares of Apple stock 60 DM ; currency: 60 Deutsche Mark £50 ; currency: 50 British pounds 50 EUR ; currency: 50 Euros (or use appropriate symbol) @end smallexample Ledger will examine the first use of any commodity to determine how that commodity should be printed on reports. It pays attention to whether the name of commodity was separated from the amount, whether it came before or after, the precision used in specifying the amount, whether thousand marks were used, etc. This is done so that printing the commodity looks the same as the way you use it. An account may contain multiple commodities, in which case it will have separate totals for each. For example, if your brokerage account contains both cash, gold, and several stock quantities, the balance might look like: @smallexample $200.00 100.00 AU AAPL 40 BORL 100 FEQTX 50 Assets:Brokerage @end smallexample This balance report shows how much of each commodity is in your brokerage account. Sometimes, you will want to know the current street value of your balance, and not the commodity totals. For this to happen, you must specify what the current price is for each commodity. The price can be any commodity, in which case the balance will be computed in terms of that commodity. The usual way to specify prices is with a price history file, which might look like this: @smallexample @c input:validate P 2004/06/21 02:18:01 FEQTX $22.49 P 2004/06/21 02:18:01 BORL $6.20 P 2004/06/21 02:18:02 AAPL $32.91 P 2004/06/21 02:18:02 AU $400.00 @end smallexample @findex --price-db @var{FILE} @findex --market Specify the price history to use with the @option{--price-db @var{FILE}} option, with the @option{--market (-V)} option to report in terms of current market value: @smallexample $ ledger --price-db prices.db -V balance brokerage @end smallexample The balance for your brokerage account will be reported in US dollars, since the prices database uses that currency. @smallexample $40880.00 Assets:Brokerage @end smallexample You can convert from any commodity to any other commodity. Let's say you had $5000 in your checking account, and for whatever reason you wanted to know how many ounces of gold that would buy, in terms of the current price of gold: @smallexample $ ledger -X AU balance checking @end smallexample The result of this command might be: @smallexample 12.50 AU Assets:Checking @end smallexample @menu * Commodity price histories:: * Commodity equivalences:: @end menu @node Commodity price histories, Commodity equivalences, Commodities and Currencies, Commodities and Currencies @subsection Commodity price histories Whenever a commodity is purchased using a different commodity (such as a share of common stock using dollars), it establishes a price for that commodity on that day. It is also possible, by recording price details in a ledger file, to specify other prices for commodities at any given time. Such price transactions might look like those below: @smallexample @c input:validate P 2004/06/21 02:17:58 TWCUX $27.76 P 2004/06/21 02:17:59 AGTHX $25.41 P 2004/06/21 02:18:00 OPTFX $39.31 P 2004/06/21 02:18:01 FEQTX $22.49 P 2004/06/21 02:18:02 AAPL $32.91 @end smallexample By default, ledger will not consider commodity prices when generating its various reports. It will always report balances in terms of the commodity total, rather than the current value of those commodities. To enable pricing reports, use one of the commodity reporting options. @node Commodity equivalences, , Commodity price histories, Commodities and Currencies @subsection Commodity equivalences Sometimes a commodity has several forms which are all equivalent. An example of this is time. Whether tracked in terms of minutes, hours or days, it should be possible to convert between the various forms. Doing this requires the use of commodity equivalences. For example, you might have the following two postings, one which transfers an hour of time into a @samp{Billable} account, and another which decreases the same account by ten minutes. The resulting report will indicate that fifty minutes remain: @smallexample @c input:DF3FEBE 2005/10/01 Work done for company Billable:Client 1h Project:XYZ 2005/10/02 Return ten minutes to the project Project:XYZ 10m Billable:Client @end smallexample Reporting the balance for this ledger file produces: @smallexample @c command:DF3FEBE $ ledger --no-total balance Billable Project @end smallexample @smallexample @c output:DF3FEBE 50.0m Billable:Client -50.0m Project:XYZ @end smallexample @findex C This example works because ledger already knows how to handle seconds, minutes and hours, as part of its time tracking support. Defining other equivalences is simple. The following is an example that creates data equivalences, helpful for tracking bytes, kilobytes, megabytes, and more: @smallexample @c input:validate C 1.00 Kb = 1024 b C 1.00 Mb = 1024 Kb C 1.00 Gb = 1024 Mb C 1.00 Tb = 1024 Gb @end smallexample Each of these definitions correlates a commodity (such as @samp{Kb}) and a default precision, with a certain quantity of another commodity. In the above example, kilobytes are reported with two decimal places of precision and each kilobyte is equal to 1024 bytes. Equivalence chains can be as long as desired. Whenever a commodity would report as a decimal amount (less than @samp{1.00}), the next smallest commodity is used. If a commodity could be reported in terms of a higher commodity without resulting to a partial fraction, then the larger commodity is used. @node Accounts and Inventories, Understanding Equity, Commodities and Currencies, Principles of Accounting with Ledger @section Accounts and Inventories Since Ledger's accounts and commodity system is so flexible, you can have accounts that don't really exist, and use commodities that no one else recognizes. For example, let's say you are buying and selling various items in EverQuest, and want to keep track of them using a ledger. Just add items of whatever quantity you wish into your EverQuest account: @smallexample @c input:48F4E47 9/29 Get some stuff at the Inn Places:Black's Tavern -3 Apples Places:Black's Tavern -5 Steaks EverQuest:Inventory @end smallexample Now your EverQuest:Inventory has 3 apples and 5 steaks in it. The amounts are negative, because you are taking @emph{from} Black's Tavern in order to add to your Inventory account. Note that you don't have to use @samp{Places:Black's Tavern} as the source account. You could use @samp{EverQuest:System} to represent the fact that you acquired them online. The only purpose for choosing one kind of source account over another is to generate more informative reports later on. The more you know, the better the analysis you can perform. If you later sell some of these items to another player, the transaction would look like: @smallexample @c input:48F4E47 10/2 Sturm Brightblade EverQuest:Inventory -2 Steaks EverQuest:Inventory 15 Gold @end smallexample Now you've turned 2 steaks into 15 gold, courtesy of your customer, Sturm Brightblade. @smallexample @c command:48F4E47 $ ledger balance EverQuest @end smallexample @smallexample @c output:48F4E47 3 Apples 15 Gold 3 Steaks EverQuest:Inventory @end smallexample @node Understanding Equity, Dealing with Petty Cash, Accounts and Inventories, Principles of Accounting with Ledger @section Understanding Equity The most confusing transaction in any ledger will be your equity account---because starting balances can't come out of nowhere. When you first start your ledger, you will likely already have money in some of your accounts. Let's say there's $100 in your checking account; then add a transaction to your ledger to reflect this amount. Where will the money come from? The answer: your equity. @smallexample @c input:validate 10/2 Opening Balance Assets:Checking $100.00 Equity:Opening Balances @end smallexample But what is equity? You may have heard of equity when people talked about house mortgages, as ``the part of the house that you own''. Basically, equity is like the value of something. If you own a car worth $5000, then you have $5000 in equity in that car. In order to turn that car (a commodity) into a cash flow, or a credit to your bank account, you will have to debit the equity by selling it. When you start a ledger, you probably already have a net worth. Your net worth is your current equity. By transferring the money in the ledger from your equity to your bank accounts, you are crediting the ledger account based on your prior equity. That is why, when you look at the balance report, you will see a large negative number for Equity that never changes: Because that is what you were worth (what you debited from yourself in order to start the ledger) before the money started moving around. If the total positive value of your assets is greater than the absolute value of your starting equity, it means you are making money. Clear as mud? Keep thinking about it. Until you figure it out, put @code{not Equity} at the end of your balance command, to remove the confusing figure from the total. @node Dealing with Petty Cash, Working with multiple funds and accounts, Understanding Equity, Principles of Accounting with Ledger @section Dealing with Petty Cash Something that stops many people from keeping a ledger at all is the insanity of tracking small cash expenses. They rarely generate a receipt, and there are often a lot of small postings, rather than a few large ones, as with checks. One solution is: don't bother. Move your spending to a debit card, but in general ignore cash. Once you withdraw it from the ATM, mark it as already spent to an @samp{Expenses:Cash} category: @smallexample @c input:validate 2004/03/15 ATM Expenses:Cash $100.00 Assets:Checking @end smallexample If at some point you make a large cash expense that you want to track, just @emph{move} the amount of the expense from @samp{Expenses:Cash} into the target account: @smallexample @c input:validate 2004/03/20 Somebody Expenses:Food $65.00 Expenses:Cash @end smallexample This way, you can still track large cash expenses, while ignoring all of the smaller ones. @node Working with multiple funds and accounts, , Dealing with Petty Cash, Principles of Accounting with Ledger @section Working with multiple funds and accounts There are situations when the accounts you're tracking are different between your clients and the financial institutions where money is kept. An example of this is working as the treasurer for a religious institution. From the secular point of view, you might be working with three different accounts: @itemize @item Checking @item Savings @item Credit Card @end itemize From a religious point of view, the community expects to divide its resources into multiple ``funds'', from which it makes purchases or reserves resources for later: @itemize @item School fund @item Building fund @item Community fund @end itemize The problem with this kind of setup is that, when you spend money, it comes from two or more places at once: the account and the fund. And yet, the correlation of amounts between funds and accounts is rarely one-to-one. What if the school fund has @samp{$500.00}, but @samp{$400.00} of that comes from Checking, and @samp{$100.00} from Savings? Traditional finance packages require that the money reside in only one place. But there are really two ``views'' of the data: from the account point of view and from the fund point of view---yet both sets should reflect the same overall expenses and cash flow. It's simply where the money resides that differs. This situation can be handled in one of two ways. The first is using virtual postings to represent the fact that money is moving to and from two kind of accounts at the same time: @smallexample @c input:396F24E 2004/03/20 Contributions Assets:Checking $500.00 Income:Donations 2004/03/25 Distribution of donations [Funds:School] $300.00 [Funds:Building] $200.00 [Assets:Checking] $-500.00 @end smallexample The use of square brackets in the second transaction ensures that the virtual postings balance to zero. Now money can be spent directly from a fund at the same time as money is drawn from a physical account: @smallexample @c input:396F24E 2004/03/25 Payment for books (paid from Checking) Expenses:Books $100.00 Assets:Checking $-100.00 (Funds:School) $-100.00 @end smallexample The use of round brackets creates a virtual posting without ensuring a balance to zero. When reports are generated, by default they'll appear in terms of the funds. In this case, you will likely want to mask out your @samp{Assets} account, because otherwise the balance won't make much sense: @smallexample @c command:396F24E $ ledger --no-total bal not ^Assets @end smallexample @smallexample @c output:396F24E $100.00 Expenses:Books $400.00 Funds $200.00 Building $200.00 School $-500.00 Income:Donations @end smallexample @findex --real If the @option{--real} option is used, the report will be in terms of the real accounts: @smallexample @c command:2F1CB75,with_input:396F24E $ ledger --real --no-total bal @end smallexample @smallexample @c output:2F1CB75 $400.00 Assets:Checking $100.00 Expenses:Books $-500.00 Income:Donations @end smallexample If more asset accounts are needed as the source of a posting, just list them as you would normally, for example: @smallexample @c input:validate 2004/03/25 Payment for books (paid from Checking) Expenses:Books $100.00 Assets:Checking $-50.00 Liabilities:Credit Card $-50.00 (Funds:School) $-100.00 @end smallexample The second way of tracking funds is to use transaction codes. In this respect the codes become like virtual accounts that embrace the entire set of postings. Basically, we are associating a transaction with a fund by setting its code. Here are two transactions that deposit money into, and spend money from, the @samp{Funds:School} fund: @smallexample @c input:AD068BA 2004/03/25 (Funds:School) Donations Assets:Checking $100.00 Income:Donations 2004/03/25 (Funds:Building) Donations Assets:Checking $20.00 Income:Donations 2004/04/25 (Funds:School) Payment for books Expenses:Books $50.00 Assets:Checking @end smallexample Note how the accounts now relate only to the real accounts, and any balance or register reports will reflect this. That the transactions relate to a particular fund is kept only in the code. @findex --payee=code @findex --by-payee How does this become a fund report? By using the @option{--payee=code} option, you can generate a register report where the payee for each posting shows the code. Alone, this is not terribly interesting; but when combined with the @option{--by-payee (-P)} option, you will now see account subtotals for any postings related to a specific fund. So, to see the current monetary balances of all funds, the command would be: @smallexample @c command:AD068BA $ ledger --payee=code -P reg ^Assets @end smallexample @smallexample @c output:AD068BA 04-Mar-25 Funds:Building Assets:Checking $20.00 $20.00 04-Mar-25 Funds:School Assets:Checking $50.00 $70.00 @end smallexample Or to see a particular fund's expenses, the @samp{School} fund in this case: @smallexample @c command:E30B2FC,with_input:AD068BA $ ledger --payee=code -P reg ^Expenses and code School @end smallexample @smallexample @c output:E30B2FC 04-Apr-25 Funds:School Expenses:Books $50.00 $50.00 @end smallexample Both approaches yield different kinds of flexibility, depending on how you prefer to think of your funds: as virtual accounts, or as tags associated with particular transactions. Your own tastes will decide which is best for your situation. @node Keeping a Journal, Transactions, Principles of Accounting with Ledger, Top @chapter Keeping a Journal The most important part of accounting is keeping a good journal. If you have a good journal, tools can be written to work whatever mathematical tricks you need to better understand your spending patterns. Without a good journal, no tool, however smart, can help you. The Ledger program aims at making journal transactions as simple as possible. Since it is a command-line tool, it does not provide a user interface for keeping a journal. If you require a user interface to maintain journal transactions GnuCash is a good alternative. If you are not using GnuCash, but a text editor to maintain your journal, read on. Ledger has been designed to make data transactions as simple as possible, by keeping the journal format easy, and also by automagically determining as much information as possible based on the nature of your transactions. For example, you do not need to tell Ledger about the accounts you use. Any time Ledger sees a posting involving an account it knows nothing about, it will create it@footnote{This also means if you misspell an account it will end up getting counted separately from what you intended. An Emacs major mode @uref{https://github.com/ledger/ledger-mode/, ledger-mode} provides tab completion for automatically filling in account names.}. If you use a commodity that is new to Ledger, it will create that commodity, and determine its display characteristics (placement of the symbol before or after the amount, display precision, etc.) based on how you used the commodity in the posting. @menu * The Most Basic Entry:: * Starting up:: * Structuring your Accounts:: * Commenting on your Journal:: * Currency and Commodities:: * Keeping it Consistent:: * Journal Format:: * Converting from other formats:: * Archiving Previous Years:: @end menu @node The Most Basic Entry, Starting up, Keeping a Journal, Keeping a Journal @section The Most Basic Entry Here is the Pacific Bell example from above, given as a Ledger posting, with the addition of a check number: @smallexample @c input:validate 9/29 (1023) Pacific Bell Expenses:Utilities:Phone $23.00 Assets:Checking $-23.00 @end smallexample As you can see, it is very similar to what would be written on paper, minus the computed balance totals, and adding in account names that work better with Ledger's scheme of things. In fact, since Ledger is smart about many things, you don't need to specify the balanced amount, if it is the same as the first line: @smallexample @c input:validate 9/29 (1023) Pacific Bell Expenses:Utilities:Phone $23.00 Assets:Checking @end smallexample For this transaction, Ledger will figure out that $-23.00 must come from @samp{Assets:Checking} in order to balance the transaction. Also note the structure of the account entries. There is an implied hierarchy established by separating with colons (@pxref{Structuring your Accounts}). @cindex spaces in postings @cindex posting format details @strong{The format is very flexible and it isn't necessary that you indent and space out things exactly as shown. The only requirements are that the start of the transaction (the date typically) is at the beginning of the first line of the transaction, and the accounts are indented by at least one space. If you omit the leading spaces in the account lines Ledger will generate an error. There must be at least two spaces, or a tab, between the amount and the account. If you do not have adequate separation between the amount and the account Ledger will give an error and stop calculating.} @node Starting up, Structuring your Accounts, The Most Basic Entry, Keeping a Journal @section Starting up @cindex initial equity @cindex beginning ledger @cindex opening balance Unless you have recently arrived from another planet, you already have a financial state. You need to capture that financial state so that Ledger has a starting point. At some convenient point in time you knew the balances and outstanding obligation of every financial account you have. Those amounts form the basis of the opening entry for ledger. For example if you chose the beginning of 2011 as the date to start tracking finances with ledger, your opening balance entry could look like this: @smallexample @c input:validate 2011/01/01 * Opening Balance Assets:Joint Checking $800.14 Assets:Other Checking $63.44 Assets:Savings $2805.54 Assets:Investments:401K:Deferred 100.0000 VIFSX @@ $80.5227 Assets:Investments:401K:Matching 50.0000 VIFSX @@ $83.7015 Assets:Investments:IRA 250.0000 VTHRX @@ $20.5324 Liabilities:Mortgage $-175634.88 Liabilities:Car Loan $-3494.26 Liabilities:Visa -$1762.44 Equity:Opening Balances @end smallexample There is nothing special about the name ``Opening Balances'' as the payee of the account name, anything convenient that you understand will work. @node Structuring your Accounts, Commenting on your Journal, Starting up, Keeping a Journal @section Structuring your Accounts @cindex accounts, naming @cindex naming accounts There really are no requirements for how you do this, but to preserve your sanity we suggest some very basic structure to your accounting system. At the highest level you have five sorts of accounts: @enumerate @item Expenses: where money goes, @item Assets: where money sits, @item Income: where money comes from, @item Liabilities: money you owe, @item Equity: the real value of your property. @end enumerate Starting the structure off this way will make it simpler for you to get answers to the questions you really need to ask about your finances. Beneath these top level accounts you can have any level of detail you desire. For example, if you want to keep specific track of how much you spend on burgers and fries, you could have the following: @smallexample @c input:validate Expenses:Food:Hamburgers and Fries @end smallexample @node Commenting on your Journal, Currency and Commodities, Structuring your Accounts, Keeping a Journal @section Commenting on your Journal @cindex comments, characters @cindex block comments @cindex comments, block Comments are generally started using a @samp{;}. However, in order to increase compatibility with other text manipulation programs and methods, four additional comment characters are valid if used at the beginning of a line: @samp{#}, @samp{|}, and @samp{*} and @samp{%}. Block comments can be made by use @code{comment} ... @code{end comment}. @smallexample @c input:validate ; This is a single line comment, # and this, % and this, | and this, * and this. comment This is a block comment with multiple lines end comment @end smallexample There are several forms of comments within a transaction, for example: @smallexample @c input:validate ; this is a global comment that is not applied to a specific transaction ; it can start with any of the five characters but is not included in the ; output from 'print' or 'output' 2011/12/11 Something Sweet ; German Chocolate Cake ; :Broke Diet: Expenses:Food $10.00 ; Friends: The gang Assets:Credit Union:Checking @end smallexample @noindent The first comment is global and Ledger will not attach it to any specific transactions. The comments within the transaction must all start with @samp{;} and are preserved as part of the transaction. The @samp{:} indicates meta-data and tags (@pxref{Metadata}). @node Currency and Commodities, Keeping it Consistent, Commenting on your Journal, Keeping a Journal @section Currency and Commodities @cindex currency @cindex commodity Ledger is agnostic when it comes to how you value your accounts. Dollars, Euros, Pounds, Francs, Shares etc. are all just ``commodities''. Holdings in stocks, bonds, mutual funds and other financial instruments can be labeled using whatever is convenient for you (stock ticker symbols are suggested for publicly traded assets).@footnote{You can track @emph{anything}, even time or distance traveled. As long as it cannot be created or destroyed inside your accounting system.} For the rest of this manual, we will only use the word ``commodities'' when referring to the units on a transaction value. This is fundamentally different than many common accounting packages, which assume the same currency throughout all of your accounts. This means if you typically operate in Euros, but travel to the US and have some expenses, you would have to do the currency conversion @emph{before} you made the entry into your financial system. With ledger this is not required. In the same journal you can have entries in any or all commodities you actually hold. You can use the reporting capabilities to convert all commodities to a single commodity for reporting purposes without ever changing the underlying entry. For example, the following entries reflect transactions made for a business trip to Europe from the US: @smallexample @c input:82150D9 2011/09/23 Cash in Munich Assets:Cash €50.00 Assets:Checking $-66.00 2011/09/24 Dinner in Munich Expenses:Business:Travel €35.00 Assets:Cash @end smallexample This says that $66.00 came out of checking and turned into 50 Euros. The implied exchange rate was $1.32. Then 35.00 Euros were spent on Dinner in Munich. Running a ledger balance report shows: @smallexample @c command:82150D9 $ ledger -f example.dat bal @end smallexample @smallexample @c output:82150D9 $-66.00 €15.00 Assets €15.00 Cash $-66.00 Checking €35.00 Expenses:Business:Travel -------------------- $-66.00 €50.00 @end smallexample The top two lines show my current assets as $-66.00 in checking (in this very short example I didn't establish opening an opening balance for the checking account) and €15.00. After spending on dinner I have €15.00 in my wallet. The bottom line balances to zero, but is shown in two lines since we haven't told ledger to convert commodities. @menu * Naming Commodities:: * Buying and Selling Stock:: * Fixing Lot Prices:: * Complete control over commodity pricing:: @end menu @node Naming Commodities, Buying and Selling Stock, Currency and Commodities, Currency and Commodities @subsection Naming Commodities Commodity names can have any character, including white-space. However, if you include white-space or numeric characters, the commodity name must be enclosed in double quotes @samp{"}: @smallexample @c input:validate 1999/06/09 ! Achat Actif:SG PEE STK 49.957 "Arcancia Équilibre 454" Actif:SG PEE STK $-234.90 2000/12/08 ! Achat Actif:SG PEE STK 215.796 "Arcancia Équilibre 455" Actif:SG PEE STK $-10742.54 @end smallexample Please note that, for querying quoted commodities, the quotes need to be escaped, as follows: @smallexample @c command:15453B3 $ ledger -f d reg -l 'commodity == "\"Arcancia Équilibre 454\""' @end smallexample @node Buying and Selling Stock, Fixing Lot Prices, Naming Commodities, Currency and Commodities @subsection Buying and Selling Stock @cindex buying stock Buying stock is a typical example that many will use that involves multiple commodities in the same transaction. The type of the share (AAPL for Apple Inc.) and the share purchase price in the currency unit you made the purchase in ($ for AAPL). Yes, the typical convention is as follows: @smallexample @c input:validate 2004/05/01 Stock purchase Assets:Broker 50 AAPL @@ $30.00 Expenses:Broker:Commissions $19.95 Assets:Broker $-1,519.95 @end smallexample This assumes you have a brokerage account that is capable of managing both liquid and commodity assets. Now, on the day of the sale: @smallexample @c input:validate 2005/08/01 Stock sale Assets:Broker -50 AAPL @{$30.00@} @@ $50.00 Expenses:Broker:Commissions $19.95 Income:Capital Gains $-1,000.00 Assets:Broker $2,480.05 @end smallexample @noindent You can, of course, elide the amount of the last posting. It is there for clarity's sake. The @samp{@{$30.00@}} is a lot price. You can also use a lot date, @samp{[2004/05/01]}, or both, in case you have several lots of the same price/date and your taxation model is based on longest-held-first. @node Fixing Lot Prices, Complete control over commodity pricing, Buying and Selling Stock, Currency and Commodities @subsection Fixing Lot Prices @cindex fixing lot prices @cindex consumable commodity pricing Commodities that you keep in order to sell at a later time have a variable value that fluctuates with the market prices. Commodities that you consume should not fluctuate in value, but stay at the lot price they were purchased at. As an extension of ``lot pricing'', you can fix the per-unit price of a commodity. For example, say you buy 10 gallons of gas at $1.20. In future ``value'' reports, you don't want these gallons reported in terms of today's price, but rather the price when you bought it. At the same time, you also want other kinds of commodities---like stocks--- reported in terms of today's price. This is supported as follows: @smallexample @c input:validate 2009/01/01 Shell Expenses:Gasoline 11 GAL @{=$2.299@} Assets:Checking @end smallexample This transaction actually introduces a new commodity, @samp{GAL @{=$2.29@}}, whose market value disregards any future changes in the price of gasoline. If you do not want price fixing, you can specify this same transaction in one of two ways, both equivalent (note the lack of the equal sign compared to the transaction above): @smallexample @c input:validate 2009/01/01 Shell Expenses:Gasoline 11 GAL @{$2.299@} Assets:Checking 2009/01/01 Shell Expenses:Gasoline 11 GAL @@ $2.299 Assets:Checking @end smallexample There is no difference in meaning between these two forms. Why do both exist, you ask? To support things like this: @smallexample @c input:validate 2009/01/01 Shell Expenses:Gasoline 11 GAL @{=$2.299@} @@ $2.30 Assets:Checking @end smallexample This transaction says that you bought 11 gallons priced at $2.299 per gallon at a @emph{cost to you} of $2.30 per gallon. Ledger auto-generates a balance posting in this case to Equity:Capital Losses to reflect the 1.1 cent difference, which is then balanced by Assets:Checking because its amount is null. @node Complete control over commodity pricing, , Fixing Lot Prices, Currency and Commodities @subsection Complete control over commodity pricing @findex --market @findex --exchange "@var{COMMODITY} [, @var{COMMODITY}, ...]" Ledger allows you to have very detailed control over how your commodities are valued. You can fine tune the results given using the @option{--market} or @option{--exchange @var{COMMODITY}} options. There are now several points of interception; you can specify the valuation method: @enumerate @item on a commodity itself, @item on a posting, via metadata (effect is largely the same as #1), @item on an xact, which then applies to all postings in that xact, @item on any posting via an automated transaction, @item on a per-account basis, @item on a per-commodity basis, @item by changing the journal default of @code{market}. @end enumerate Fixated pricing (such as @samp{@{=$20@}}) still plays a role in this scheme. As far as valuation goes, it's shorthand for writing @samp{((s,d,t -> market($20,d,t)))}. A valuation function receives three arguments: @table @code @item source A string identifying the commodity whose price is being asked for (example: @samp{EUR}). @item date The reference date the price should be relative. @item target A string identifying the ``target'' commodity, or the commodity the returned price should be in. This argument is null if @option{--market} was used instead of @option{--exchange @var{COMMODITY}}. @end table The valuation function should return an amount. If you've written your function in Python, you can return something like @samp{Amount("$100")}. If the function returns an explicit value, that value is always used, regardless of the commodity, the date, or the desired target commodity. For example, @smallexample @c input:validate define myfunc_seven(s, d, t) = 7 EUR @end smallexample In order to specify a fixed price, but still valuate that price into the target commodity, use something like this: @smallexample @c input:validate define myfunc_five(s, d, t) = market(5 EUR, d, t) @end smallexample The @code{value} directive sets the valuation used for all commodities used in the rest of the data stream. This is the fallback, if nothing more specific is found. @smallexample @c input:validate value myfunc_seven @end smallexample You can set a specific valuation function on a per-commodity basis. Instead of defining a function, you can also pass a lambda. @smallexample @c input:validate commodity $ value s, d, t -> 6 EUR @end smallexample Each account can also provide a default valuation function for any commodities transferred to that account. @smallexample @c input:validate account Expenses:Food5 value myfunc_five @end smallexample The metadata field @samp{Value}, if found, overrides the valuation function on a transaction-wide or per-posting basis. @smallexample @c input:validate = @@XACT and Food ; Value:: 8 EUR (Equity) $1 = @@POST and Dining (Expenses:Food9) $1 ; Value:: 9 EUR @end smallexample Lastly, you can specify the valuation function/value for any specific amount using the @samp{(( ))} commodity annotation. @smallexample @c input:814A366 2012-03-02 KFC Expenses:Food2 $1 ((2 EUR)) Assets:Cash2 2012-03-03 KFC Expenses:Food3 $1 ; Value:: 3 EUR Assets:Cash3 2012-03-04 KFC ; Value:: 4 EUR Expenses:Food4 $1 Assets:Cash4 2012-03-05 KFC Expenses:Food5 $1 Assets:Cash5 2012-03-06 KFC Expenses:Food6 $1 Assets:Cash6 2012-03-07 KFC Expenses:Food7 1 CAD Assets:Cash7 2012-03-08 XACT Expenses:Food8 $1 Assets:Cash8 2012-03-09 POST Expenses:Dining9 $1 Assets:Cash9 @end smallexample @smallexample @c command:814A366 $ ledger reg -V food @end smallexample @smallexample @c output:814A366 12-Mar-02 KFC Expenses:Food2 2 EUR 2 EUR 12-Mar-03 KFC Expenses:Food3 3 EUR 5 EUR 12-Mar-04 KFC Expenses:Food4 4 EUR 9 EUR 12-Mar-05 KFC Expenses:Food5 $1 $1 9 EUR 12-Mar-06 KFC Expenses:Food6 $1 $2 9 EUR 12-Mar-07 KFC Expenses:Food7 1 CAD $2 1 CAD 9 EUR 12-Mar-08 XACT Expenses:Food8 $1 $3 1 CAD 9 EUR @end smallexample @node Keeping it Consistent, Journal Format, Currency and Commodities, Keeping a Journal @section Keeping it Consistent @findex --strict @findex accounts Sometimes Ledger's flexibility can lead to difficulties. Using a freeform text editor to enter transactions makes it easy to keep the data, but also easy to enter accounts or payees inconsistently or with spelling errors. In order to combat inconsistency you can define allowable accounts and payees. For simplicity, create a separate text file and define accounts and payees like @smallexample @c input:validate account Expenses account Expenses:Utilities @end smallexample Using the @option{--strict} option will cause Ledger to complain if any accounts are not previously defined: @smallexample $ ledger bal --strict Warning: "FinanceData/Master.dat", line 6: Unknown account 'Liabilities:Tithe Owed' Warning: "FinanceData/Master.dat", line 8: Unknown account 'Liabilities:Tithe Owed' Warning: "FinanceData/Master.dat", line 15: Unknown account 'Allocation:Equities:Domestic' @end smallexample If you have a large Ledger register already created use the @command{accounts} command to get started: @smallexample @c command:validate $ ledger accounts >> Accounts.dat @end smallexample @noindent You will have to edit this file to add the @code{account} directive in front of every line. @node Journal Format, Converting from other formats, Keeping it Consistent, Keeping a Journal @section Journal Format The ledger file format is quite simple, but also very flexible. It supports many options, though typically the user can ignore most of them. They are summarized below. @menu * Transactions and Comments:: * Command Directives:: @end menu @node Transactions and Comments, Command Directives, Journal Format, Journal Format @subsection Transactions and Comments The initial character of each line determines what the line means, and how it should be interpreted. Allowable initial characters are: @table @code @item NUMBER A line beginning with a number denotes a transaction. It may be followed by any number of lines, each beginning with white-space, to denote the transaction's account postings. The format of the first line is: @smallexample DATE[=EDATE] [*|!] [(CODE)] DESC @end smallexample If @samp{*} appears after the date (with optional effective date), it indicates the transaction is ``cleared'', which can mean whatever the user wants it to mean. If @samp{!} appears after the date, it indicates the transaction is ``pending''; i.e., tentatively cleared from the user's point of view, but not yet actually cleared. If a @code{CODE} appears in parentheses, it may be used to indicate a check number, or the type of the posting. Following these is the payee, or a description of the posting. The format of each following posting is: @smallexample ACCOUNT AMOUNT [; NOTE] @end smallexample The @code{ACCOUNT} may be surrounded by parentheses if it is a virtual posting, or square brackets if it is a virtual posting that must balance. The @code{AMOUNT} can be followed by a per-unit posting cost, by specifying @code{@@ AMOUNT}, or a complete posting cost with @code{@@@@ AMOUNT}. Lastly, the @code{NOTE} may specify an actual and/or effective date for the posting by using the syntax @code{[ACTUAL_DATE]} or @code{[=EFFECTIVE_DATE]} or @code{[ACTUAL_DATE=EFFECTIVE_DATE]} (@pxref{Virtual postings}). @item P @findex --download @findex P @cindex historical prices Specifies a historical price for a commodity. These are usually found in a pricing history file (see the @option{--download (-Q)} option). The syntax is: @smallexample P DATE SYMBOL PRICE @end smallexample @item = @findex = @cindex automated transaction @cindex transaction, automated An automated transaction. A value expression must appear after the equal sign. After this initial line there should be a set of one or more postings, just as if it were a normal transaction. If the amounts of the postings have no commodity, they will be applied as multipliers to whichever real posting is matched by the value expression (@pxref{Automated Transactions}). @item ~ @findex ~ @cindex periodic transaction @cindex transaction, periodic A periodic transaction. A period expression must appear after the tilde. After this initial line there should be a set of one or more postings, just as if it were a normal transaction. @item ; # % | * @findex comment @cindex comments A line beginning with a semicolon, pound, percent, bar or asterisk indicates a comment, and is ignored. Comments will not be returned in a ``print'' response. @item indented ; @cindex tags If the semicolon is indented and occurs inside a transaction, it is parsed as a persistent note for its preceding category. These notes or tags can be used to augment the reporting and filtering capabilities of Ledger. @end table @node Command Directives, , Transactions and Comments, Journal Format @subsection Command Directives @findex --strict @findex --pedantic @table @code @item beginning of line Command directives must occur at the beginning of a line. Use of @samp{!} and @samp{@@} is deprecated. @item account @findex account @cindex pre-declare account Pre-declare valid account names. This only has an effect if @option{--strict} or @option{--pedantic} is used (see below). The @code{account} directive supports several optional sub-directives, if they immediately follow the account directive and if they begin with whitespace: @smallexample @c input:validate account Expenses:Food note This account is all about the chicken! alias food payee ^(KFC|Popeyes)$ check commodity == "$" assert commodity == "$" eval print("Hello!") default @end smallexample The @code{note} sub-directive associates a textual note with the account. This can be accessed later using the @code{note} value expression function in any account context. The @code{alias} sub-directive, which can occur multiple times, allows the alias to be used in place of the full account name anywhere that account names are allowed. The @code{payee} sub-directive, which can occur multiple times, provides regexes that identify the account if that payee is encountered and an account within its transaction ends in the name "Unknown". Example: @smallexample @c input:validate 2012-02-27 KFC Expenses:Unknown $10.00 ; Read now as "Expenses:Food" Assets:Cash @end smallexample The @code{check} and @code{assert} directives warn or raise an error (respectively) if the given value expression evaluates to false within the context of any posting. The @code{eval} directive evaluates the value expression in the context of the account at the time of definition. At the moment this has little value. The @code{default} directive specifies that this account should be used as the ``balancing account'' for any future transactions that contain only a single posting. @item apply account @findex apply account @c instance_t::master_account_directive Sets the root for all accounts following this directive. Ledger supports a hierarchical tree of accounts. It may be convenient to keep two ``root accounts''. For example you may be tracking your personal finances and your business finances. In order to keep them separate you could preface all personal accounts with @samp{personal:} and all business accounts with @samp{business:}. You can easily split out large groups of transactions without manually editing them using the account directive. For example: @smallexample @c input:validate apply account Personal 2011/11/15 Supermarket Expenses:Groceries $ 50.00 Assets:Checking @end smallexample Would result in all postings going into @samp{Personal:Expenses:Groceries} and @samp{Personal:Assets:Checking} until an @samp{end apply account} directive was found. @item apply fixed @findex fixed @cindex fixated prices @c instance_t::fixed_directive in textual.cc A fixed block is used to set fixated prices (@pxref{Fixated prices and costs}) for a series of transactions. It's purely a typing saver, for use when entering many transactions with fixated prices. Thus, the following: @smallexample @c input:validate apply fixed CAD $0.90 2012-04-10 Lunch in Canada Assets:Wallet -15.50 CAD Expenses:Food 15.50 CAD 2012-04-11 Second day Dinner in Canada Assets:Wallet -25.75 CAD Expenses:Food 25.75 CAD end apply fixed @end smallexample is equivalent to this: @smallexample @c input:validate 2012-04-10 Lunch in Canada Assets:Wallet -15.50 CAD @{=$0.90@} Expenses:Food 15.50 CAD @{=$0.90@} 2012-04-11 Second day Dinner in Canada Assets:Wallet -25.75 CAD @{=$0.90@} Expenses:Food 25.75 CAD @{=$0.90@} @end smallexample @item alias @findex alias @cindex account, alias @c instance_t::alias_directive Define an alias for an account name. If you have a deeply nested tree of accounts, it may be convenient to define an alias, for example: @smallexample @c input:94A99E8 alias Dining=Expenses:Entertainment:Dining alias Checking=Assets:Credit Union:Joint Checking Account 2011/11/28 YummyPalace Dining $10.00 Checking @end smallexample The aliases are only in effect for transactions read in after the alias is defined and are affected by @code{account} directives that precede them. @smallexample @c command:94A99E8 $ ledger bal --no-total ^Exp @end smallexample @smallexample @c output:94A99E8 $10.00 Expenses:Entertainment:Dining @end smallexample With the option @option{--recursive-aliases}, aliases can refer to other aliases, the following example produces exactly the same transactions and account names as the preceding one: @smallexample @c input:83E1FB3 alias Entertainment=Expenses:Entertainment alias Dining=Entertainment:Dining alias Checking=Assets:Credit Union:Joint Checking Account 2011/11/30 ChopChop Dining $10.00 Checking @end smallexample @smallexample @c command:83E1FB3 $ ledger balance --no-total --recursive-aliases ^Exp @end smallexample @smallexample @c output:83E1FB3 $10.00 Expenses:Entertainment:Dining @end smallexample The option @option{--no-aliases} completely disables alias expansion. All accounts are read verbatim as they are in the ledger file. @item assert @findex assert @cindex assertions @c instance_t::assert_directive An assertion can throw an error if a condition is not met during Ledger's run. @smallexample assert <VALUE EXPRESSION BOOLEAN RESULT> @end smallexample @item bucket @anchor{bucket} @findex bucket @cindex bucket @c instance_t::default_account_directive Defines the default account to use for balancing transactions. Normally, each transaction has at least two postings, which must balance to zero. Ledger allows you to leave one posting with no amount and automatically balance the transaction in the posting. The @code{bucket} allows you to fill in all postings and automatically generate an additional posting to the bucket account balancing the transaction. If any transaction is unbalanced, it will automatically be balanced against the @code{bucket} account. The following example sets @samp{Assets:Checking} as the bucket: @smallexample @c input:validate bucket Assets:Checking 2011/01/25 Tom's Used Cars Expenses:Auto $ 5,500.00 2011/01/27 Book Store Expenses:Books $20.00 2011/12/01 Sale Assets:Checking:Business $ 30.00 @end smallexample @item capture @c instance_t::account_mapping_directive @findex capture @findex print @findex register Directs Ledger to replace any account matching a regex with the given account. For example: @smallexample @c input:validate capture Expenses:Deductible:Medical Medical @end smallexample Would cause any posting with @samp{Medical} in its name to be replaced with @samp{Expenses:Deductible:Medical}. Ledger will display the mapped payees in @command{print} and @command{register} reports. @item check @findex check @cindex assertions @c instance_t::check_directive in textual.cc A check issues a warning if a condition is not met during Ledger's run. @smallexample check <VALUE EXPRESSION BOOLEAN RESULT> @end smallexample @item comment @findex comment @cindex comments @c instance_t::comment_directive in textual.cc Start a block comment, closed by @code{end comment}. @item commodity @findex commodity @cindex pre-declare commodity Pre-declare commodity names. This only has an effect if @option{--strict} or @option{--pedantic} is used (see below). @smallexample @c input:validate commodity $ commodity CAD @end smallexample The @code{commodity} directive supports several optional sub-directives, if they immediately follow the commodity directive and---if they are on successive lines---begin with whitespace: @smallexample @c input:validate commodity $ note American Dollars format $1,000.00 nomarket alias USD default @end smallexample The @code{note} sub-directive associates a textual note with the commodity. At present this has no value other than documentation. The @code{format} sub-directive gives you a way to tell Ledger how to format this commodity. In the future, using this directive will disable Ledger's observation of other ways that commodity is used, and will provide the ``canonical'' representation. The @code{nomarket} sub-directive states that the commodity's price should never be auto-downloaded. The @code{alias} sub-directive states that any commodity matching this symbol is to use the commodity declared in this block. The @code{default} sub-directive marks this as the ``default'' commodity. @item define @findex define @c instance_t::define_directive in textual.cc Allows you to define value expressions for future use. For example: @smallexample @c input:validate define var_name=$100 2011/12/01 Test Expenses (var_name*4) Assets @end smallexample The posting will have a cost of $400. @item end @findex end @c instance_t::end_directive in textual.cc Closes block commands like @code{apply} or @code{comment}. @item expr @findex expr Same as @code{eval}. @item include @findex include @c instance_t::include_directive in textual.cc Include the stated file as if it were part of the current file. The file name can contain a wildcard (@samp{*}) to refer to multiple files (e.g. @samp{bank/*.ledger}). @item payee @findex payee @c instance_t::payee_alias_mapping_directive in textual.cc @c instance_t::payee_uuid_mapping_directive in textual.cc @findex print @findex register The @code{payee} directive supports two optional sub-directives, if they immediately follow the payee directive and---if it is on a successive line---begins with whitespace: @smallexample @c input:validate payee KFC alias KENTUCKY FRIED CHICKEN uuid 2a2e21d434356f886c84371eebac6e44f1337fda @end smallexample The @code{alias} sub-directive provides a regex which, if it matches a parsed payee, the declared payee name is substituted: @smallexample @c input:validate 2012-02-27 KENTUCKY FRIED CHICKEN ; will be read as being 'KFC' @end smallexample The @code{uuid} sub-directive specifies that a transaction with exactly the uuid given should have the declared payee name substituted: @smallexample @c input:validate 2014-05-13 UNHELPFUL PAYEE ; will be read as being 'KFC' ; UUID: 2a2e21d434356f886c84371eebac6e44f1337fda @end smallexample Ledger will display the mapped payees in @command{print} and @command{register} reports. @item apply tag @findex apply tag @c instance_t::tag_directive in textual.cc Allows you to designate a block of transactions and assign the same tag to all. Tags can have values and may be nested. @smallexample @c input:validate apply tag hastag apply tag nestedtag: true 2011/01/25 Tom's Used Cars Expenses:Auto $ 5,500.00 ; :nobudget: Assets:Checking 2011/01/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard end apply tag 2011/12/01 Sale Assets:Checking:Business $ 30.00 Income:Sales end apply tag @end smallexample @noindent is the equivalent of: @smallexample @c input:validate 2011/01/25 Tom's Used Cars ; :hastag: ; nestedtag: true Expenses:Auto $ 5,500.00 ; :nobudget: Assets:Checking 2011/01/27 Book Store ; :hastag: ; nestedtag: true Expenses:Books $20.00 Liabilities:MasterCard 2011/12/01 Sale ; :hastag: Assets:Checking:Business $ 30.00 Income:Sales @end smallexample @c TODO: the following paragraph seems to be false, the automated tests @c fail, if anything appears after end apply tag. @c Note that anything following @code{end apply tag} is ignored. Placing @c the name of the tag that is being closed is a simple way to keep @c track. @item tag @findex tag @cindex pre-declare tag Pre-declares tag names. This only has an effect if @option{--strict} or @option{--pedantic} is used (see below). @smallexample @c input:validate tag Receipt tag CSV @end smallexample The @code{tag} directive supports two optional sub-directives, if they immediately follow the tag directive and---if on a successive line---begin with whitespace: @smallexample @c input:validate tag Receipt check value =~ /pattern/ assert value != "foobar" @end smallexample The @code{check} and @code{assert} sub-directives warn or error (respectively) if the given value expression evaluates to false within the context of any use of the related tag. In such a context, ``value'' is bound to the value of the tag (which may be something else but a string if typed metadata is used!). Such checks or assertions are not called if no value is given. @item test @findex test @cindex comments @c instance_t::comment_directive in textual.cc This is a synonym for @code{comment} and must be closed by an @code{end} tag. @item year @findex year @anchor{year} @c instance_t::year_directive in textual.cc Denotes the year used for all subsequent transactions that give a date without a year. The year should appear immediately after the directive, for example: @code{year 2004}. This is useful at the beginning of a file, to specify the year for that file. If all transactions specify a year, however, this command has no effect. @end table The following single letter commands may be at the beginning of a line alone, for backwards compatibility with older Ledger versions. @table @code @item A @findex A @findex bucket @xref{bucket}. @item Y @findex Y @findex year @xref{year}. @item N SYMBOL @findex N Indicates that pricing information is to be ignored for a given symbol, nor will quotes ever be downloaded for that symbol. Useful with a home currency, such as the dollar @samp{$}. It is recommended that these pricing options be set in the price database file, which defaults to @file{~/.pricedb}. The syntax for this command is: @smallexample @c input:validate N SYMBOL @end smallexample @item D AMOUNT @findex xact @findex D Specifies the default commodity to use, by specifying an amount in the expected format. The @command{xact} command will use this commodity as the default when none other can be determined. This command may be used multiple times, to set the default flags for different commodities; whichever is seen last is used as the default commodity. For example, to set US dollars as the default commodity, while also setting the thousands flag and decimal flag for that commodity, use: @smallexample @c input:validate D $1,000.00 @end smallexample @item C AMOUNT1 = AMOUNT2 @findex C Specifies a commodity conversion, where the first amount is given to be equivalent to the second amount. The first amount should use the decimal precision desired during reporting: @smallexample @c input:validate C 1.00 Kb = 1024 bytes @end smallexample @item I, i, O, o, b, h @findex I @findex i @findex O @findex o @findex b @findex h These four relate to timeclock support, which permits Ledger to read timelog files. See timeclock's documentation for more info on the syntax of its timelog files. @end table @node Converting from other formats, Archiving Previous Years, Journal Format, Keeping a Journal @section Converting from other formats @cindex csv importing There are numerous tools to help convert various formats to a Ledger file. Most banks will generate a comma separated values file that can easily be parsed into Ledger format using one of those tools. Some of the most popular tools are: @itemize @item @code{ledger convert download.csv} @item @code{hledger -f checking.csv print} @item @uref{https://github.com/quentinsf/icsv2ledger, @code{icsv2ledger}} @item @uref{https://github.com/tazzben/csvToLedger, @code{csvToLedger}} @item @uref{https://launchpad.net/csv2ledger, @code{CSV2Ledger}} @end itemize @noindent Directly pulling information from banks is outside the scope of Ledger's function. @node Archiving Previous Years, , Converting from other formats, Keeping a Journal @section Archiving Previous Years @findex equity @findex print After a while, your journal can get to be pretty large. While this will not slow down Ledger---it's designed to process journals very quickly---things can start to feel ``messy''; and it's a universal complaint that when finances feel messy, people avoid them. Thus, archiving the data from previous years into their own files can offer a sense of completion, and freedom from the past. But how to best accomplish this with the ledger program? There are two commands that make it very simple: @command{print}, and @command{equity}. Let's take an example file, with data ranging from year 2000 until 2004. We want to archive years 2000 and 2001 to their own file, leaving 2002--2004 in the current file. So, use @command{print} to output all the earlier transactions to a file called @file{ledger-old.dat}: @smallexample $ ledger -f ledger.dat -b 2000 -e 2002 print > ledger-old.dat @end smallexample Note that @option{-e} limits output to transactions @emph{before} the date specified. To delete older data from the current ledger file, use @command{print} again, this time specifying year 2002 as the starting date: @smallexample $ ledger -f ledger.dat -b 2002 print > x $ mv x ledger.dat @end smallexample However, now the current file contains @emph{only} postings from 2002 onward, which will not yield accurate present-day balances, because the net income from previous years is no longer being tallied. To compensate for this, we must append an equity report for the old ledger at the beginning of the new one: @smallexample $ ledger -f ledger-old.dat equity > equity.dat $ cat equity.dat ledger.dat > x $ mv x ledger.dat $ rm equity.dat @end smallexample Now the balances reported from @file{ledger.dat} are identical to what they were before the data was split. How often should you split your ledger? You never need to, if you don't want to. Even eighty years of data will not slow down ledger much, and that's just using present day hardware! Or, you can keep the previous and current year in one file, and each year before that in its own file. It's really up to you, and how you want to organize your finances. For those who also keep an accurate paper trail, it might be useful to archive the older years to their own files, then burn those files to a CD to keep with the paper records---along with any electronic statements received during the year. In the arena of organization, just keep in mind this maxim: Do whatever keeps you doing it. @node Transactions, Building Reports, Keeping a Journal, Top @chapter Transactions @menu * Basic format:: * Eliding amounts:: * Auxiliary dates:: * Codes:: * Transaction state:: * Transaction notes:: * Metadata:: * Virtual postings:: * Expression amounts:: * Balance verification:: * Posting cost:: * Explicit posting costs:: * Posting cost expressions:: * Total posting costs:: * Virtual posting costs:: * Commodity prices:: * Prices versus costs:: * Fixated prices and costs:: * Lot dates:: * Lot notes:: * Lot value expressions:: * Automated Transactions:: @end menu @node Basic format, Eliding amounts, Transactions, Transactions @section Basic format The most basic form of transaction is: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash $-20.00 @end smallexample This transaction has a date, a payee or description, a target account (the first posting), and a source account (the second posting). Each posting specifies what action is taken related to that account. A transaction can have any number of postings: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash $-10.00 Liabilities:Credit $-10.00 @end smallexample @node Eliding amounts, Auxiliary dates, Basic format, Transactions @section Eliding amounts The first thing you can do to make things easier is elide amounts. That is, if exactly one posting has no amount specified, Ledger will infer the inverse of the other postings' amounts: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash $-10.00 Liabilities:Credit ; same as specifying $-10 @end smallexample @noindent If the other postings use multiple commodities, Ledger will copy the empty posting N times and fill in the negated values of the various commodities: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 Expenses:Tips $2.00 Assets:Cash EUR -10.00 Assets:Cash GBP -10.00 Liabilities:Credit @end smallexample @noindent This transaction is identical to writing: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 Expenses:Tips $2.00 Assets:Cash EUR -10.00 Assets:Cash GBP -10.00 Liabilities:Credit $-22.00 Liabilities:Credit EUR 10.00 Liabilities:Credit GBP 10.00 @end smallexample @node Auxiliary dates, Codes, Eliding amounts, Transactions @section Auxiliary dates @findex --aux-date You can associate a second date with a transaction by following the primary date with an equals sign: @smallexample @c input:validate 2012-03-10=2012-03-08 KFC Expenses:Food $20.00 Assets:Cash $-20.00 @end smallexample What this auxiliary date means is entirely up to you. The only use Ledger has for it is that if you specify @option{--aux-date} (or @option{--effective}), then all reports and calculations (including pricing) will use the auxiliary date as if it were the primary date. Note that the @option{--aux-date} option is an alias for @option{--effective}; for more details on effective dates @pxref{Effective Dates}. @node Codes, Transaction state, Auxiliary dates, Transactions @section Codes A transaction can have a textual ``code''. This has no meaning and is only displayed by the print command. Checking accounts often use codes like DEP, XFER, etc., as well as check numbers. This is to give you a place to put those codes: @smallexample @c input:validate 2012-03-10 (#100) KFC Expenses:Food $20.00 Assets:Checking @end smallexample @node Transaction state, Transaction notes, Codes, Transactions @section Transaction state @findex --cleared @findex --uncleared @findex --pending A transaction can have a ``state'': cleared, pending, or uncleared. The default is uncleared. To mark a transaction cleared, put an asterisk @samp{*} after the date, before the code or payee: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food $20.00 Assets:Cash @end smallexample @noindent To mark it pending, use a @samp{!}: @smallexample @c input:validate 2012-03-10 ! KFC Expenses:Food $20.00 Assets:Cash @end smallexample What these mean is entirely up to you. The @option{--cleared} option limits reports to only cleared items, while @option{--uncleared} shows both uncleared and pending items, and @option{--pending} shows only pending items. I use cleared to mean that I've reconciled the transaction with my bank statement, and pending to mean that I'm in the middle of a reconciliation. When you clear a transaction, that's really just shorthand for clearing all of its postings. That is: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food $20.00 Assets:Cash @end smallexample @noindent Is the same as writing: @smallexample @c input:validate 2012-03-10 KFC * Expenses:Food $20.00 * Assets:Cash @end smallexample @noindent You can mark individual postings as cleared or pending, in case one ``side'' of the transaction has cleared, but the other hasn't yet: @smallexample @c input:validate 2012-03-10 KFC Liabilities:Credit $100.00 * Assets:Checking @end smallexample @node Transaction notes, Metadata, Transaction state, Transactions @section Transaction notes After the payee, and after at least one tab or two spaces (or a space and a tab), which Ledger calls a ``hard separator'', you may introduce a note about the transaction using the @samp{;} character: @smallexample @c input:validate 2012-03-10 * KFC ; yum, chicken... Expenses:Food $20.00 Assets:Cash @end smallexample @noindent Notes can also appear on the next line, so long as that line begins with whitespace: @smallexample @c input:validate 2012-03-10 * KFC ; yum, chicken... ; and more notes... Expenses:Food $20.00 Assets:Cash 2012-03-10 * KFC ; just these notes... Expenses:Food $20.00 Assets:Cash @end smallexample A transaction's note is shared by all its postings. This becomes significant when querying for metadata (see below). To specify that a note belongs only to one posting, place it after a hard separator after the amount, or on its own line preceded by whitespace: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food $20.00 ; posting #1 note Assets:Cash ; posting #2 note, extra indentation is optional @end smallexample @node Metadata, Virtual postings, Transaction notes, Transactions @section Metadata @c TODO add cindex @c TODO https://groups.google.com/d/msg/ledger-cli/2csLPcHJ3ak/a17jOClzLTUJ @c > Is there a way to produce a register report that lists all the transaction @c > that contain a certain tag, and sort them based on the value of the tag? @c ledger reg --sort "tag('foo')" %foo @c ledger reg --group-by "tag('Employer)" Remboursement:Employer and tag Employer @c > Is it possible to get subtotals for each tag value? @c ledger --group-by "tag('foo')" bal @c TODO https://groups.google.com/d/msg/ledger-cli/K9NBhNlVnYc/TDYDAWhOA5EJ One of Ledger's more powerful features is the ability to associate typed metadata with postings and transactions (by which I mean all of a transaction's postings). This metadata can be queried, displayed, and used in calculations. The are two forms of metadata: plain tags, and tag/value pairs. @menu * Metadata tags:: * Metadata values:: * Typed metadata:: @end menu @node Metadata tags, Metadata values, Metadata, Metadata @subsection Metadata tags To tag an item, put any word not containing whitespace between two colons inside a comment: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food $20.00 Assets:Cash ; :TAG: @end smallexample You can gang up multiple tags by sharing colons: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food $20.00 Assets:Cash ; :TAG1:TAG2:TAG3: @end smallexample @node Metadata values, Typed metadata, Metadata tags, Metadata @subsection Metadata values To associate a value with a tag, use the syntax ``Key: Value'', where the value can be any string of characters. Whitespace is needed after the colon, and cannot appear in the Key: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food $20.00 Assets:Cash ; MyTag: This is just a bogus value for MyTag @end smallexample @node Typed metadata, , Metadata values, Metadata @subsection Typed metadata If a metadata tag ends in ::, its value will be parsed as a value expression and stored internally as a value rather than as a string. For example, although I can specify a date textually like so: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food $20.00 Assets:Cash ; AuxDate: 2012/02/30 @end smallexample @noindent This date is just a string, and won't be parsed as a date unless its value is used in a date-context (at which time the string is parsed into a date automatically every time it is needed as a date). If on the other hand I write this: @smallexample 2012-03-10 * KFC Expenses:Food $20.00 Assets:Cash ; AuxDate:: [2012/02/30] @end smallexample @noindent Then it is parsed as a date only once, and during parsing of the journal file, which would let me know right away that it is an invalid date. @menu * Payee metadata:: @end menu @node Payee metadata, , Typed metadata, Metadata @subsection Payee metadata @cindex Payee metadata @findex register @findex payees @findex --by-payee ``Payee'' is a special metadata field. If set on a posting, it will be used as the payee name for that posting. This affects the @command{register} report, the @command{payees} report, and the @option{--by-payee} option. This is useful when for example you deposit 4 checks at a time to the bank. On the bank statement, there is just one amount @samp{$400}, but you can specify from whom each check came, as shown by example below: @smallexample @c input:9B43E57 2010-06-17 Sample Assets:Bank $400.00 Income:Check1 $-100.00 ; Payee: Person One Income:Check2 $-100.00 ; Payee: Person Two Income:Check3 $-100.00 ; Payee: Person Three Income:Check4 $-100.00 ; Payee: Person Four @end smallexample When reporting with @smallexample @c command:9B43E57 $ ledger reg @end smallexample it appears as: @smallexample @c output:9B43E57 10-Jun-17 Sample Assets:Bank $400.00 $400.00 Person One Income:Check1 $-100.00 $300.00 Person Two Income:Check2 $-100.00 $200.00 Person Three Income:Check3 $-100.00 $100.00 Person Four Income:Check4 $-100.00 0 @end smallexample This shows that they are all in the same transaction (which is why the date is not repeated), but they have different payees now. If using the @option{--strict} or @option{--pedantic} options, you must declare this tag to avoid warnings and errors. @node Virtual postings, Expression amounts, Metadata, Transactions @section Virtual postings @findex --real Ordinarily, the amounts of all postings in a transaction must balance to zero. This is non-negotiable. It's what double-entry accounting is all about! But there are some tricks up Ledger's sleeve... You can use virtual accounts to transfer amounts to an account on the sly, bypassing the balancing requirement. The trick is that these postings are not considered ``real'', and can be removed from all reports using @option{--real}. To specify a virtual account, surround the account name with parentheses: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food $20.00 Assets:Cash (Budget:Food) $-20.00 @end smallexample If you want, you can state that virtual postings @emph{should} balance against one or more other virtual postings by using brackets (which look ``harder'') rather than parentheses: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food $20.00 Assets:Cash [Budget:Food] $-20.00 [Equity:Budgets] $20.00 @end smallexample @node Expression amounts, Balance verification, Virtual postings, Transactions @section Expression amounts An amount is a numerical figure with a commodity, but it can also be any value expression. To indicate this, surround the amount expression with parentheses: @smallexample @c input:validate 2012-03-10 * KFC Expenses:Food ($10.00 + $20.00) ; Ledger adds it up for you Assets:Cash @end smallexample @node Balance verification, Posting cost, Expression amounts, Transactions @section Balance verification @findex --permissive @menu * Balance assertions:: * Balance assignments:: * Resetting a balance:: * Balancing transactions:: @end menu If at the end of a posting's amount (and after the cost too, if there is one) there is an equals sign, then Ledger will verify that the total value for that account as of that posting matches the amount specified. See @option{--permissive} option to relax the balance assertions checks. There are two forms of this features: balance assertions, and balance assignments. Note that both of these are processed while parsing the given ledger files. Hence the order in which these are evaluated is the order in which they appear in the ledger file. The date or effective date of the transactions and postings that contain the balance assertions or balance assignments is therefore irrelevant for the the evaluation of the balance assertions and balance assignments. This may be confusing to people for whom a date order is more intuitive. @node Balance assertions, Balance assignments, Balance verification, Balance verification @subsection Balance assertions A balance assertion has this general form: @smallexample 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash $-20.00 = $500.00 @end smallexample This simply asserts that after subtracting $20.00 from Assets:Cash, that the resulting total matches $500.00. If not, it is an error. The assertion has an effect only on the specified commodity. If an account has multiple commodities, then only the one asserted is verified: @smallexample 2012-03-10 KFC New York Expenses:Food $20.00 Assets:Cash $-20.00 = $500.00 2012-03-11 KFC Montreal Expenses:Food 15.00 CAD Assets:Cash -15.00 CAD = $500.00 @end smallexample In this case, the amount in USD of cash (which has not changed) is validated. Nothing is asserted about the current amount of Canadian dollars in @samp{Asset:Cash}. @subsubsection Special assertion value 0 The only value that can be asserted without a commodity is @samp{0}. This results in a cross-commodities assertion, which makes it possible to assert that an account is totally empty. @smallexample 2012-03-09 Fill Wallet Revenue $20.00 Revenue 15.00 CAD Assets:Cash 2012-03-10 KFC New York Expenses:Food $20.00 Assets:Cash $-20.00 2012-03-11 KFC Montreal Expenses:Food 15.00 CAD Assets:Cash -15.00 CAD = 0 @end smallexample The last transaction will assert that we are out of cash of any sort. @node Balance assignments, Resetting a balance, Balance assertions, Balance verification @subsection Balance assignments A balance assignment has this form: @smallexample 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash = $500.00 @end smallexample This sets the amount of the second posting to whatever it would need to be for the total in @samp{Assets:Cash} to be $500.00 after the posting. If the resulting amount is not $-20.00 in this case, it is an error. @node Resetting a balance, Balancing transactions, Balance assignments, Balance verification @subsection Resetting a balance Say your book-keeping has gotten a bit out of date, and your Ledger balance no longer matches your bank balance. You can create an adjustment transaction using balance assignments: @smallexample @c input:validate 2012-03-10 Adjustment Assets:Cash = $500.00 Equity:Adjustments @end smallexample Since the second posting is also null, its value will become the inverse of whatever amount is generated for the first posting. This is the only time in ledger when more than one posting's amount may be empty---and then only because it's not truly empty, it is indirectly provided by the balance assignment's value. @node Balancing transactions, , Resetting a balance, Balance verification @subsection Balancing transactions @findex --empty As a consequence of all the above, consider the following transaction: @smallexample 2012-03-10 My Broker [Assets:Brokerage] = 10 AAPL @end smallexample What this says is: set the amount of the posting to whatever value is needed so that @samp{Assets:Brokerage} contains 10 AAPL. Then, because this posting must balance, ensure that its value is zero. This can only be true if Assets:Brokerage does indeed contain 10 AAPL at that point in the input file. A balanced virtual transaction is used simply to indicate to Ledger that this is not a ``real'' transaction. It won't appear in any reports anyway (unless you use a register report with @option{--empty}). @node Posting cost, Explicit posting costs, Balance verification, Transactions @section Posting cost When you transfer a commodity from one account to another, sometimes it gets transformed during the transaction. This happens when you spend money on gas, for example, which transforms dollars into gallons of gasoline, or dollars into stocks in a company. In those cases, Ledger will remember the ``cost'' of that transaction for you, and can use it during reporting in various ways. Here's an example of a stock purchase: @smallexample @c input:validate 2012-03-10 My Broker Assets:Brokerage 10 AAPL Assets:Brokerage:Cash $-500.00 @end smallexample This is different from transferring 10 AAPL shares from one account to another, in this case you are @emph{exchanging} one commodity for another. The resulting posting's cost is $50.00 per share. @node Explicit posting costs, Posting cost expressions, Posting cost, Transactions @section Explicit posting costs You can make any posting's cost explicit using the @samp{@@} symbol after the amount or amount expression: @smallexample @c input:validate 2012-03-10 My Broker Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash $-500.00 @end smallexample When you do this, since Ledger can now figure out the balancing amount from the first posting's cost, you can elide the other amount: @smallexample @c input:validate 2012-03-10 My Broker Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash @end smallexample @menu * Primary and secondary commodities:: @end menu @node Primary and secondary commodities, , Explicit posting costs, Explicit posting costs @subsection Primary and secondary commodities @findex --market @findex --exchange "@var{COMMODITY} [, @var{COMMODITY}, ...]" It is a general convention within Ledger that the ``top'' postings in a transaction contain the target accounts, while the final posting contains the source account. Whenever a commodity is exchanged like this, the commodity moved to the target account is considered ``secondary'', while the commodity used for purchasing and tracked in the cost is ``primary''. Said another way, whenever Ledger sees a posting cost of the form "AMOUNT @@ AMOUNT", the commodity used in the second amount is marked ``primary''. The only meaning a primary commodity has is that the @option{--market (-V)} flag will never convert a primary commodity into any other commodity. @option{--exchange @var{COMMODITY} (-X)} still will, however. @node Posting cost expressions, Total posting costs, Explicit posting costs, Transactions @section Posting cost expressions Just as you can have amount expressions, you can have posting expressions: @smallexample @c input:validate 2012-03-10 My Broker Assets:Brokerage 10 AAPL @@ ($500.00 / 10) Assets:Brokerage:Cash @end smallexample You can even have both: @smallexample @c input:validate 2012-03-10 My Broker Assets:Brokerage (5 AAPL * 2) @@ ($500.00 / 10) Assets:Brokerage:Cash @end smallexample @node Total posting costs, Virtual posting costs, Posting cost expressions, Transactions @section Total posting costs The cost figure following the @samp{@@} character specifies the @emph{per-unit} price for the commodity being transferred. If you'd like to specify the total cost instead, use @samp{@@@@}: @smallexample @c input:validate 2012-03-10 My Broker Assets:Brokerage 10 AAPL @@@@ $500.00 Assets:Brokerage:Cash @end smallexample Ledger reads this as if you had written: @smallexample @c input:validate 2012-03-10 My Broker Assets:Brokerage 10 AAPL @@ ($500.00 / 10) Assets:Brokerage:Cash @end smallexample @node Virtual posting costs, Commodity prices, Total posting costs, Transactions @section Virtual posting costs Normally whenever a commodity exchange like this happens, the price of the exchange (such as $50 per share of AAPL, above) is recorded in Ledger's internal price history database. To prevent this from happening in the case of an exceptional transaction, surround the @samp{@@} or @samp{@@@@} with parentheses: @smallexample @c input:validate 2012-03-10 My Brother Assets:Brokerage 1000 AAPL (@@) $1 Income:Gifts Received @end smallexample @node Commodity prices, Prices versus costs, Virtual posting costs, Transactions @section Commodity prices @findex --lot-prices When a transaction occurs that exchanges one commodity for another, Ledger records that commodity price not only within its internal price database, but also attached to the commodity itself. Usually this fact remains invisible to the user, unless you turn on @option{--lot-prices} to show these hidden price figures. For example, consider the stock sale given above: @smallexample @c input:validate 2012-03-10 My Broker Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash @end smallexample The commodity transferred into @samp{Assets:Brokerage} is not actually 10 AAPL, but rather 10 AAPL @{$50.00@}. The figure in braces after the amount is called the ``lot price''. It's Ledger's way of remembering that this commodity was transferred through an exchange, and that $50.00 was the price of that exchange. This becomes significant if you later sell that commodity again. For example, you might write this: @smallexample @c input:validate 2012-04-10 My Broker Assets:Brokerage:Cash Assets:Brokerage -10 AAPL @@ $75.00 @end smallexample And that would be perfectly fine, but how do you track the capital gains on the sale? It could be done with a virtual posting: @smallexample @c input:validate 2012-04-10 My Broker Assets:Brokerage:Cash Assets:Brokerage -10 AAPL @@ $75.00 (Income:Capital Gains) $-250.00 @end smallexample But this gets messy since capital gains income is very real, and not quite appropriate for a virtual posting. Instead, if you reference that same hidden price annotation, Ledger will figure out that the price of the shares you're selling, and the cost you're selling them at, don't balance: @smallexample 2012-04-10 My Broker Assets:Brokerage:Cash $750.00 Assets:Brokerage -10 AAPL @{$50.00@} @@ $75.00 @end smallexample This transaction will fail because the $250.00 price difference between the price you bought those shares at, and the cost you're selling them for, does not match. The lot price also identifies which shares you purchased on that prior date. @menu * Total commodity prices:: @end menu @node Total commodity prices, , Commodity prices, Commodity prices @subsection Total commodity prices As a shorthand, you can specify the total price instead of the per-share price in doubled braces. This goes well with total costs, but is not required to be used with them: @smallexample @c input:validate 2012-04-10 My Broker Assets:Brokerage:Cash $750.00 Assets:Brokerage -10 AAPL @{@{$500.00@}@} @@@@ $750.00 Income:Capital Gains $-250.00 @end smallexample It should be noted that this is a convenience only for cases where you buy and sell whole lots. The @{@{$500.00@}@} is @emph{not} an attribute of the commodity, whereas @{$50.00@} is. In fact, when you write @{@{$500.00@}@}, Ledger just divides that value by 10 and sees @{$50.00@}. So if you use the print command to look at this transaction, you'll see the single braces form in the output. The double braces price form is a shorthand only. Plus, it comes with dangers. This works fine: @smallexample @c input:validate 2012-04-10 My Broker Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash $-500.00 2012-04-10 My Broker Assets:Brokerage:Cash $375.00 Assets:Brokerage -5 AAPL @{$50.00@} @@@@ $375.00 Income:Capital Gains $-125.00 2012-04-10 My Broker Assets:Brokerage:Cash $375.00 Assets:Brokerage -5 AAPL @{$50.00@} @@@@ $375.00 Income:Capital Gains $-125.00 @end smallexample @noindent But this does not do what you might expect: @smallexample 2012-04-10 My Broker Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash $-500.00 2012-04-10 My Broker Assets:Brokerage:Cash $375.00 Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@@@ $375.00 Income:Capital Gains $-125.00 2012-04-10 My Broker Assets:Brokerage:Cash $375.00 Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@@@ $375.00 Income:Capital Gains $-125.00 @end smallexample And in cases where the amounts do not divide into whole figures and must be rounded, the capital gains figure could be off by a cent. Use with caution. @node Prices versus costs, Fixated prices and costs, Commodity prices, Transactions @section Prices versus costs Because lot pricing provides enough information to infer the cost, the following two transactions are equivalent: @smallexample 2012-04-10 My Broker Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash $-500.00 2012-04-10 My Broker Assets:Brokerage 10 AAPL @{$50.00@} Assets:Brokerage:Cash $-500.00 @end smallexample However, note that what you see in some reports may differ, for example in the print report. Functionally, however, there is no difference, and neither the register nor the balance report are sensitive to this difference. @node Fixated prices and costs, Lot dates, Prices versus costs, Transactions @section Fixated prices and costs If you bought a stock last year, and ask for its value today, Ledger will consult its price database to see what the most recent price for that stock is. You can short-circuit this lookup by ``fixing'' the price at the time of a transaction. This is done using @samp{@{=AMOUNT@}}: @smallexample 2012-04-10 My Broker Assets:Brokerage 10 AAPL @{=$50.00@} Assets:Brokerage:Cash $-500.00 @end smallexample These 10 AAPL will now always be reported as being worth $50, no matter what else happens to the stock in the meantime. Fixated prices are a special case of using lot valuation expressions (see below) to fix the value of a commodity lot. Since price annotations and costs are largely interchangeable and a matter of preference, there is an equivalent syntax for specified fixated prices by way of the cost: @smallexample 2012-04-10 My Broker Assets:Brokerage 10 AAPL @@ =$50.00 Assets:Brokerage:Cash $-500.00 @end smallexample This is the same as the previous transaction, with the same caveats found in @ref{Prices versus costs}. @node Lot dates, Lot notes, Fixated prices and costs, Transactions @section Lot dates @findex --lot-dates In addition to lot prices, you can specify lot dates and reveal them with @option{--lot-dates}. Other than that, however, they have no special meaning to Ledger. They are specified after the amount in square brackets (the same way that dates are parsed in value expressions): @smallexample 2012-04-10 My Broker Assets:Brokerage:Cash $375.00 Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] @@@@ $375.00 Income:Capital Gains $-125.00 @end smallexample @node Lot notes, Lot value expressions, Lot dates, Transactions @section Lot notes @findex --lot-notes @findex --lots You can also associate arbitrary notes for your own record keeping in parentheses, and reveal them with @option{--lot-notes}. One caveat is that the note cannot begin with an @samp{@@} character, as that would indicate a virtual cost: @smallexample 2012-04-10 My Broker Assets:Brokerage:Cash $375.00 Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] (Oh my!) @@@@ $375.00 Income:Capital Gains $-125.00 @end smallexample You can specify any combination of lot prices, dates or notes, in any order. They are all optional. To show all lot information in a report, use @option{--lots}. @node Lot value expressions, Automated Transactions, Lot notes, Transactions @section Lot value expressions Normally when you ask Ledger to display the values of commodities held, it uses a value expression called ``market'' to determine the most recent value from its price database---even downloading prices from the Internet, if @option{--download (-Q)} was specified and a suitable @file{getquote} script is found on your system. However, you can override this valuation logic by providing a commodity valuation expression in doubled parentheses. This expression must result in one of two values: either an amount to always be used as the per-share price for that commodity; or a function taking three arguments, which is called to determine that price. If you use the functional form, you can either specify a function name, or a lambda expression. Here's a function that yields the price as $10 in whatever commodity is being requested: @smallexample @c input:validate define ten_dollars(s, date, t) = market($10, date, t) @end smallexample I can now use that in a lot value expression as follows: @smallexample @c input:validate 2012-04-10 My Broker Assets:Brokerage:Cash $375.00 Assets:Brokerage -5 AAPL @{$50.00@} ((ten_dollars)) @@@@ $375.00 Income:Capital Gains $-125.00 @end smallexample Alternatively, I could do the same thing without pre-defining a function by using a lambda expression taking three arguments: @smallexample 2012-04-10 My Broker A:B:Cash $375.00 A:B -5 AAPL @{$50.00@} ((s, d, t -> market($10, date, t))) @@@@ $375.00 Income:Capital Gains $-125.00 @end smallexample The arguments passed to these functions have the following meaning: @itemize @item source The source commodity string, or an amount object. If it is a string, the return value must be an amount representing the price of the commodity identified by that string (example: @samp{$}). If it is an amount, return the value of that amount as a new amount (usually calculated as commodity price times source amount). @item date The date to use for determining the value. If null, it means no date was specified, which can mean whatever you want it to mean. @item target If not null, a string representing the desired target commodity that the commodity price, or repriced amount, should be valued in. Note that this string can be a comma-separated list, and that some or all of the commodities in that list may be suffixed with an exclamation mark, to indicate what is being desired. @end itemize In most cases, it is simplest to either use explicit amounts in your valuation expressions, or just pass the arguments down to @samp{market} after modifying them to suit your needs. @node Automated Transactions, , Lot value expressions, Transactions @section Automated Transactions An automated transaction is a special kind of transaction which adds its postings to other transactions any time one of that other transactions' postings matches its predicate. The predicate uses the same query syntax as the Ledger command-line. Consider this posting: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash @end smallexample If I write this automated transaction before it in the file: @smallexample @c input:validate = expr true Foo $50.00 Bar $-50.00 @end smallexample Then the first transaction will be modified during parsing as if I'd written this: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 Foo $50.00 Bar $-50.00 Assets:Cash $-20.00 Foo $50.00 Bar $-50.00 @end smallexample Despite this fancy logic, automated transactions themselves follow most of the same rules as regular transactions: their postings must balance (unless you use a virtual posting), you can have metadata, etc. One thing you cannot do, however, is elide amounts in an automated transaction. @menu * Amount multipliers:: * Accessing the matching posting's amount:: * Referring to the matching posting's account:: * Applying metadata to every matched posting:: * Applying metadata to the generated posting:: * State flags:: * Effective Dates:: * Periodic Transactions:: * Concrete Example of Automated Transactions:: @end menu @node Amount multipliers, Accessing the matching posting's amount, Automated Transactions, Automated Transactions @subsection Amount multipliers As a special case, if an automated transaction's posting's amount (phew) has no commodity, it is taken as a multiplier upon the matching posting's cost. For example: @smallexample @c input:validate = expr true Foo 50.00 Bar -50.00 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash @end smallexample Then the latter transaction turns into this during parsing: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 Foo $1000.00 Bar $-1000.00 Assets:Cash $-20.00 Foo $1000.00 Bar $-1000.00 @end smallexample @node Accessing the matching posting's amount, Referring to the matching posting's account, Amount multipliers, Automated Transactions @subsection Accessing the matching posting's amount If you use an amount expression for an automated transaction's posting, that expression has access to all the details of the matched posting. For example, you can refer to that posting's amount using the ``amount'' value expression variable: @smallexample @c input:validate = expr true (Foo) (amount * 2) ; same as just "2" in this case 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash @end smallexample This becomes: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 (Foo) $40.00 Assets:Cash $-20.00 (Foo) $-40.00 @end smallexample @node Referring to the matching posting's account, Applying metadata to every matched posting, Accessing the matching posting's amount, Automated Transactions @subsection Referring to the matching posting's account Sometimes you want to refer to the account that was matched in some way within the automated transaction itself. This is done by using the string @samp{$account}, anywhere within the account part of the automated posting: @smallexample @c input:validate = food (Budget:$account) 10 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash @end smallexample Becomes: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 (Budget:Expenses:Food) $200.00 Assets:Cash $-20.00 @end smallexample Keep in mind that if you are using @option{--strict} or @option{--pedantic} you will have to explicitly define an account @samp{$account} to avoid errors. @smallexample @c input:validate account $account @end smallexample @node Applying metadata to every matched posting, Applying metadata to the generated posting, Referring to the matching posting's account, Automated Transactions @subsection Applying metadata to every matched posting If the automated transaction has a transaction note, that note is copied (along with any metadata) to every posting that matches the predicate: @smallexample @c input:validate = food ; Foo: Bar (Budget:$account) 10 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash @end smallexample Becomes: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 ; Foo: Bar (Budget:Expenses:Food) $200.00 Assets:Cash $-20.00 @end smallexample @node Applying metadata to the generated posting, State flags, Applying metadata to every matched posting, Automated Transactions @subsection Applying metadata to the generated posting If the automated transaction's posting has a note, that note is carried to the generated posting within the matched transaction: @smallexample @c input:validate = food (Budget:$account) 10 ; Foo: Bar 2012-03-10 KFC Expenses:Food $20.00 Assets:Cash @end smallexample Becomes: @smallexample @c input:validate 2012-03-10 KFC Expenses:Food $20.00 (Budget:Expenses:Food) $200.00 ; Foo: Bar Assets:Cash $-20.00 @end smallexample This is slightly different from the rules for regular transaction notes, in that an automated transaction's note does not apply to every posting within the automated transaction itself, but rather to every posting it matches. @node State flags, Effective Dates, Applying metadata to the generated posting, Automated Transactions @subsection State flags Although you cannot mark an automated transaction as a whole as cleared or pending, you can mark its postings with a @samp{*} or @samp{!} before the account name, and that state flag gets carried to the generated posting. @node Effective Dates, Periodic Transactions, State flags, Automated Transactions @subsection Effective Dates @cindex effective dates @findex --effective In the real world, transactions do not take place instantaneously. Purchases can take several days to post to a bank account. And you may pay ahead for something for which you want to distribute costs. With Ledger you can control every aspect of the timing of a transaction. Say you're in business. If you bill a customer, you can enter something like @cindex effective date of invoice @smallexample @c input:validate 2008/01/01=2008/01/14 Client invoice ; estimated date you'll be paid Assets:Accounts Receivable $100.00 Income: Client name @end smallexample Then, when you receive the payment, you change it to @smallexample @c input:validate 2008/01/01=2008/01/15 Client invoice ; actual date money received Assets:Accounts Receivable $100.00 Income: Client name @end smallexample @noindent and add something like @smallexample @c input:validate 2008/01/15 Client payment Assets:Checking $100.00 Assets:Accounts Receivable @end smallexample Now @smallexample @c command:validate $ ledger --begin 2008/01/01 --end 2008/01/14 bal Income @end smallexample @noindent gives you your accrued income in the first two weeks of the year, and @smallexample @c command:validate $ ledger --effective --begin 2008/01/01 --end 2008/01/14 bal Income @end smallexample @noindent gives you your cash basis income in the same two weeks. Another use is distributing costs out in time. As an example, suppose you just prepaid into a local vegetable co-op that sustains you through the winter. It costs $225 to join the program, so you write a check. You don't want your October grocery budget to be blown because you bought food ahead, however. What you really want is for the money to be evenly distributed over the next six months so that your monthly budgets gradually take a hit for the vegetables you'll pick up from the co-op, even though you've already paid for them. @smallexample @c input:6453542 2008/10/16 * (2090) Bountiful Blessings Farm Expenses:Food:Groceries $ 37.50 ; [=2008/10/01] Expenses:Food:Groceries $ 37.50 ; [=2008/11/01] Expenses:Food:Groceries $ 37.50 ; [=2008/12/01] Expenses:Food:Groceries $ 37.50 ; [=2009/01/01] Expenses:Food:Groceries $ 37.50 ; [=2009/02/01] Expenses:Food:Groceries $ 37.50 ; [=2009/03/01] Assets:Checking @end smallexample This entry accomplishes this. Every month you'll see an automatic $37.50 deficit like you should, while your checking account really knows that it debited $225 this month. And using the @option{--effective} (or @option{--aux-date}) option, the initial date will be overridden by the effective dates. @smallexample @c command:6453542 $ ledger --effective register Groceries @end smallexample @smallexample @c output:6453542 08-Oct-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 37.50 08-Nov-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 75.00 08-Dec-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 112.50 09-Jan-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 150.00 09-Feb-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 187.50 09-Mar-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 225.00 @end smallexample Note that the @option{--aux-date} option is an alias for @option{--effective}; for a brief explanation of auxiliary date @pxref{Auxiliary dates}. @node Periodic Transactions, Concrete Example of Automated Transactions, Effective Dates, Automated Transactions @subsection Periodic Transactions @findex --budget A periodic transaction starts with a tilde @samp{~} followed by a period expression (see @ref{Period Expressions}). Periodic transactions are used for budgeting and forecasting only, they have no effect without the @option{--budget} option specified. For examples and details, @pxref{Budgeting and Forecasting}. @node Concrete Example of Automated Transactions, , Periodic Transactions, Automated Transactions @subsection Concrete Example of Automated Transactions As a Bahá'í, I need to compute Huqúqu'lláh whenever I acquire assets. It is similar to tithing for Jews and Christians, or to Zakát for Muslims. The exact details of computing Huqúqu'lláh are somewhat complex, but if you have further interest, please consult the Web. Ledger makes this otherwise difficult law very easy. Just set up an automated posting at the top of your ledger file: @smallexample @c input:C371854 ; This automated transaction will compute Huqúqu'lláh based on this ; journal's postings. Any accounts that match will affect the ; Liabilities:Huqúqu'lláh account by 19% of the value of that posting. = /^(?:Income:|Expenses:(?:Business|Rent$|Furnishings|Taxes|Insurance))/ (Liabilities:Huqúqu'lláh) 0.19 @end smallexample This automated posting works by looking at each posting in the ledger file. If any match the given value expression, 19% of the posting's value is applied to the @samp{Liabilities:Huqúqu'lláh} account. So, if $1000 is earned from @samp{Income:Salary}, $190 is added to @samp{Liabilities:Huqúqu'lláh}; if $1000 is spent on Rent, $190 is subtracted. @smallexample @c input:C371854 2003/01/01 (99) Salary Income:Salary -$1000 Assets:Checking 2003/01/01 (100) Rent Expenses:Rent $500 Assets:Checking @end smallexample The ultimate balance of Huqúqu'lláh reflects how much is owed in order to fulfill one's obligation to Huqúqu'lláh. When ready to pay, just write a check to cover the amount shown in @samp{Liabilities:Huqúqu'lláh}. That transaction would look like: @smallexample @c input:validate 2003/01/01 (101) Bahá'í Huqúqu'lláh Trust Liabilities:Huqúqu'lláh $1,000.00 Assets:Checking @end smallexample That's it. To see how much Huqúq is currently owed based on your ledger transactions, use: @smallexample @c command:C371854 $ ledger balance Liabilities:Huqúq @end smallexample @smallexample @c output:C371854 $-95 Liabilities:Huqúqu'lláh @end smallexample This works fine, but omits one aspect of the law: that Huqúq is only due once the liability exceeds the value of 19 mithqáls of gold (which is roughly 2.22 ounces). So what we want is for the liability to appear in the balance report only when it exceeds the present day value of 2.22 ounces of gold. This can be accomplished using the command: @c TODO: fix this, it doesn't work any longer @smallexample $ ledger -Q -t "/Liab.*Huquq/?(a/P@{2.22 AU@}<=@{-1.0@}&a):a" bal liab @end smallexample With this command, the current price for gold is downloaded, and the Huqúqu'lláh is reported only if its value exceeds that of 2.22 ounces of gold. If you wish the liability to be reflected in the parent subtotal either way, use this instead: @c TODO: fix this, it doesn't work any longer @smallexample $ ledger -Q -T "/Liab.*Huquq/?(O/P@{2.22 AU@}<=@{-1.0@}&O):O" bal liab @end smallexample In some cases, you may wish to refer to the account of whichever posting matched your automated transaction's value expression. To do this, use the special account name @samp{$account}: @smallexample @c input:validate = /^Some:Long:Account:Name/ [$account] -0.10 [Savings] 0.10 @end smallexample This example causes 10% of the matching account's total to be deferred to the @samp{Savings} account---as a balanced virtual posting, which may be excluded from reports by using @option{--real}. @node Building Reports, Reporting Commands, Transactions, Top @chapter Building Reports @menu * Introduction:: * Balance Reports:: * Typical queries:: * Advanced Reports:: @end menu @node Introduction, Balance Reports, Building Reports, Building Reports @section Introduction The power of Ledger comes from the incredible flexibility in its reporting commands, combined with formatting commands. Some options control what is included in the calculations, and formatting controls how it is displayed. The combinations are infinite. This chapter will show you the basics of combining various options and commands. In the next chapters you will find details about the specific commands and options. @node Balance Reports, Typical queries, Introduction, Building Reports @section Balance Reports @menu * Controlling the Accounts and Payees:: * Controlling Formatting:: @end menu @node Controlling the Accounts and Payees, Controlling Formatting, Balance Reports, Balance Reports @subsection Controlling the Accounts and Payees The balance report is the most commonly used report. The simplest invocation is: @smallexample @c command:1D00D56 $ ledger balance -f drewr3.dat @end smallexample @noindent which will print the balances of every account in your journal. @smallexample @c output:1D00D56 $ -3,804.00 Assets $ 1,396.00 Checking $ 30.00 Business $ -5,200.00 Savings $ -1,000.00 Equity:Opening Balances $ 6,654.00 Expenses $ 5,500.00 Auto $ 20.00 Books $ 300.00 Escrow $ 334.00 Food:Groceries $ 500.00 Interest:Mortgage $ -2,030.00 Income $ -2,000.00 Salary $ -30.00 Sales $ -63.60 Liabilities $ -20.00 MasterCard $ 200.00 Mortgage:Principal $ -243.60 Tithe -------------------- $ -243.60 @end smallexample Most times, this is more than you want. Limiting the results to specific accounts is as easy as entering the names of the accounts after the command: @smallexample @c command:06B2AD4 $ ledger balance -f drewr3.dat Auto MasterCard @end smallexample @smallexample @c output:06B2AD4 $ 5,500.00 Expenses:Auto $ -20.00 Liabilities:MasterCard -------------------- $ 5,480.00 @end smallexample @noindent Note the implicit logical or between @samp{Auto} and @samp{Mastercard}. If you want the entire contents of a branch of your account tree, use the highest common name in the branch: @smallexample @c command:B0468E1 $ ledger balance -f drewr3.dat Income @end smallexample @smallexample @c output:B0468E1 $ -2,030.00 Income $ -2,000.00 Salary $ -30.00 Sales -------------------- $ -2,030.00 @end smallexample You can use general regular expressions (PCRE) in nearly any place Ledger needs a string: @smallexample @c command:EAE389F $ ledger balance -f drewr3.dat ^Bo @end smallexample @smallexample @c output:EAE389F @end smallexample This first example looks for any account starting with @samp{Bo}, of which there are none. @smallexample @c command:E2AF6AD $ ledger balance -f drewr3.dat Bo @end smallexample @smallexample @c output:E2AF6AD $ 20.00 Expenses:Books @end smallexample This second example looks for any account containing @samp{Bo}, which is @samp{Expenses:Books}. @cindex limit by payees @findex --limit @var{EXPR} If you want to know exactly how much you have spent in a particular account on a particular payee, the following are equivalent: @smallexample @c command:validate $ ledger balance Auto:Fuel and Chevron @end smallexample @smallexample @c command:validate $ ledger balance --limit 'account=~/Fuel/' and 'payee=~/Chev/' @end smallexample @noindent will show you the amount expended on gasoline at Chevron. The second example is the first example of the very powerful expression language available to shape reports. The first example may be easier to remember, but learning to use the second will open up far more possibilities. If you want to exclude specific accounts from the report, you can exclude multiple accounts with parentheses: @smallexample @c command:validate $ ledger bal Expenses and not (Expenses:Drinks or Expenses:Candy or Expenses:Gifts) @end smallexample @node Controlling Formatting, , Controlling the Accounts and Payees, Balance Reports @subsection Controlling Formatting These examples all use the default formatting for the balance report. Customizing the formatting allows you to see only what you want, or interface Ledger with other programs. For examples and details, @pxref{Format Strings} and @ref{Asset Allocation}. @node Typical queries, Advanced Reports, Balance Reports, Building Reports @section Typical queries A query such as the following shows all expenses since last October, sorted by total: @smallexample @c command:validate $ ledger -b "last oct" -S T bal ^expenses @end smallexample From left to right the options mean: Show transactions since last October; sort by the absolute value of the total; and report the balance for all accounts that begin with @samp{expenses}. @menu * Reporting monthly expenses:: @end menu @node Reporting monthly expenses, , Typical queries, Typical queries @subsection Reporting monthly expenses @findex --monthly @findex --display @var{EXPR} @findex --period-sort @var{VEXPR} @findex --related @findex --subtotal The following query makes it easy to see monthly expenses, with each month's expenses sorted by the amount: @smallexample @c command:validate $ ledger -M --period-sort "(amount)" reg ^expenses @end smallexample Now, you might wonder where the money came from to pay for these things. To see that report, add @option{--related (-r)}, which shows the ``related account'' postings: @smallexample @c command:validate $ ledger -M --period-sort "(amount)" -r reg ^expenses @end smallexample But maybe this prints too much information. You might just want to see how much you're spending with your MasterCard. That kind of query requires the use of a display predicate, since the postings calculated must match @samp{^expenses}, while the postings displayed must match @samp{mastercard}. The command would be: @smallexample @c command:validate $ ledger -M -r --display 'account=~/mastercard/' reg ^expenses @end smallexample This query says: Report monthly subtotals; report the ``related account'' postings; display only related postings whose account matches @samp{mastercard}, and base the calculation on postings matching @samp{^expenses}. This works just as well for reporting the overall total, too: @smallexample @c command:validate $ ledger -s -r --display "account=~/mastercard/" reg ^expenses @end smallexample The @option{--subtotal (-s)} option subtotals all postings, just as @option{--monthly (-M)} subtotaled by the month. The running total in both cases is off, however, since a display expression is being used. @node Advanced Reports, , Typical queries, Building Reports @section Advanced Reports @menu * Asset Allocation:: * Visualizing with Gnuplot:: @end menu @node Asset Allocation, Visualizing with Gnuplot, Advanced Reports, Advanced Reports @subsection Asset Allocation A very popular method of managing portfolios is to control the percent allocation of assets by certain categories. The mix of categories and the weights applied to them vary by investing philosophy, but most follow a similar pattern. Tracking asset allocation in ledger is not difficult but does require some additional effort to describe how the various assets you own contribute to the asset classes you want to track. In our simple example we assume you want to apportion your assets into the general categories of domestic and international equities (stocks) and a combined category of bonds and cash. For illustrative purposes, we will use several publicly available mutual funds from Vanguard. The three funds we will track are the Vanguard 500 IDX FD Signal (VIFSX), the Vanguard Target Retirement 2030 (VTHRX), and the Vanguard Short Term Federal Fund (VSGBX). Each of these funds allocates assets to different categories of the investment universe and in different proportions. When you buy a share of VTHRX, that share is partially invested in equities, and partially invested in bonds and cash. Below is the asset allocation for each of the instruments listed above: @multitable @columnfractions .2 .2 .3 .3 @item @tab Domestic @tab Global @tab @item Symbol @tab Equity @tab Equity @tab bonds/cash @item VIFSX @tab 100% @tab @tab @item VTHRX @tab 24.0% @tab 56.3% @tab 19.7% @item VSGBX @tab @tab @tab 100% @end multitable These numbers are available from the prospectus of any publicly available mutual fund. Of course a single stock issue is 100% equity and a single bond issue is 100% bonds. We track purchases of specific investments using the symbol of that investment as its commodity. How do we tell Ledger that a share of VTHRX is 24% Domestic equity? Enter automatic transactions and virtual accounts. At the top of our ledger we enter automatic transactions that describe these proportions to Ledger. In the same entries we set up virtual accounts that let us separate these abstract calculations from our actual balances. For the three instruments listed above, those automatic transactions would look like: @smallexample @c input:582C8C2 = expr ( commodity == 'VIFSX' ) (Allocation:Equities:Domestic) 1.000 = expr ( commodity == 'VTHRX' ) (Allocation:Equities:Global) 0.240 (Allocation:Equities:Domestic) 0.563 (Allocation:Bonds/Cash) 0.197 = expr ( commodity == 'VBMFX') (Allocation:Bonds/Cash) 1.000 2015-01-01 Buy VIFSX Assets:Broker 100 VIFSX Assets:Cash $-10000 2015-01-01 Buy VTHRX Assets:Broker 10 VTHRX Assets:Cash $-10000 2015-01-01 Buy VBMFX Assets:Broker 1 VBMFX Assets:Cash $-10000 @end smallexample How do these work? First the @samp{=} sign at the beginning of the line tells ledger this is an automatic transaction to be applied when the condition following the @samp{=} is true. After the @samp{=} sign is a value expression (@pxref{Value Expressions}) that returns true any time a posting contains the commodity of interest. The following line gives the proportions (not percentages) of each unit of commodity that belongs to each asset class. Whenever Ledger sees a buy or sell of a particular commodity it will credit or debit these virtual accounts with that proportion of the number of shares moved. Now that Ledger understands how to distribute the commodities amongst the various asset classes how do we get a report that tells us our current allocation? Using the balance command and some tricky formatting! @smallexample @c command:582C8C2 ledger bal Allocation --current --format "\ %-17((depth_spacer)+(partial_account))\ %10(percent(market(display_total), market(parent.total)))\ %16(market(display_total))\n%/" @end smallexample Which yields: @smallexample @c output:582C8C2 Allocation 100.00% $30000 Bonds/Cash 39.90% $11970 Equities 60.10% $18030 Domestic 86.69% $15630 Global 13.31% $2400 @end smallexample Let's look at the Ledger invocation a bit closer. The command above is split into lines for clarity. The first line is very vanilla Ledger asking for the current balances of the account in the ``Allocation'' tree, using a special formatter. @cindex depth_spacer @cindex display_total @cindex parent.total The magic is in the formatter. The second line simply tells Ledger to print the partial account name indented by its depth in the tree. The third line is where we calculate and display the percentages. The @code{display_total} command gives the values of the total calculated for the account in this line. The @code{parent.total} command gives the total for the next level up in the tree. @code{percent} formats their ratio as a percentage. The fourth line tells ledger to display the current market value of the line. The last two characters @samp{%/} tell Ledger what to do for the last line, in this case, nothing. @node Visualizing with Gnuplot, , Asset Allocation, Advanced Reports @subsection Visualizing with Gnuplot @cindex plotting @cindex Gnuplot @findex --amount-data @findex --total-data @findex --limit @var{EXPR} @findex --display @var{EXPR} If you have the ``Gnuplot'' program installed, you can graph any of the above register reports. The script to do this is included in the ledger distribution, and is named @file{contrib/report}. Install @file{report} anywhere along your @env{PATH}, and then use @file{report} instead of @file{ledger} when doing a register report. The only thing to keep in mind is that you must specify @option{--amount-data (-j)} or @option{--total-data (-J)} to indicate whether ``Gnuplot'' should plot the amount, or the running total. For example, this command plots total monthly expenses made on your MasterCard. @smallexample $ report -j -M -r --display "account =~ /mastercard/" reg ^expenses @end smallexample The @file{report} script is a very simple Bourne shell script, that passes a set of scripted commands to ``Gnuplot''. Feel free to modify the script to your liking, since you may prefer histograms to line plots, for example. Here are some useful plots: @smallexample report -j -M reg ^expenses # monthly expenses report -J reg checking # checking account balance report -J reg ^income ^expenses # cash flow report # net worth report, ignoring non-$ postings report -J -l "Ua>=@{\$0.01@}" reg ^assets ^liab # net worth report starting last February. the use of a display # predicate (-d) is needed, otherwise the balance will start at # zero, and thus the y-axis will not reflect the true balance report -J -l "Ua>=@{\$0.01@}" -d "d>=[last feb]" reg ^assets ^liab @end smallexample The last report uses both a calculation predicate @option{--limit @var{EXPR} (-l)} and a display predicate @option{--display @var{EXPR} (-d)}. The calculation predicate limits the report to postings whose amount is greater than or equal to $0.01 (which can only happen if the posting amount is in dollars). The display predicate limits the transactions @emph{displayed} to just those since last February, even though those transactions from before will be computed as part of the balance. @node Reporting Commands, Command-Line Syntax, Building Reports, Top @chapter Reporting Commands @menu * Primary Financial Reports:: Reports in other formats:: Reports about * Reports in other Formats:: * Reports about your Journals:: @end menu @node Primary Financial Reports, Reports in other Formats, Reporting Commands, Reporting Commands @section Primary Financial Reports @menu * The @command{balance} command:: * The @command{equity} command:: * The @command{register} command:: * The @command{print} command:: @end menu @node The @command{balance} command, The @command{equity} command, Primary Financial Reports, Primary Financial Reports @subsection The @command{balance} command @findex balance The @command{balance} command reports the current balance of all accounts. It accepts a list of optional regexes, which confine the balance report to the matching accounts. If an account contains multiple types of commodities, each commodity's total is reported separately. @node The @command{equity} command, The @command{register} command, The @command{balance} command, Primary Financial Reports @subsection The @command{equity} command @findex equity The @command{equity} command prints out account balances as if they were transactions. This makes it easy to establish the starting balances for an account, such as when @ref{Archiving Previous Years}. @node The @command{register} command, The @command{print} command, The @command{equity} command, Primary Financial Reports @subsection The @command{register} command @findex register @findex --amount-data @findex --total-data The @command{register} command displays all the postings occurring in a single account, line by line. The account regex must be specified as the only argument to this command. If any regexes occur after the required account name, the register will contain only those postings that match, which makes it very useful for hunting down a particular posting. The output from @command{register} is very close to what a typical checkbook, or single-account ledger, would look like. It also shows a running balance. The final running balance of any register should always be the same as the current balance of that account. If you have ``Gnuplot'' installed, you may plot the amount or running total of any register by using the script @file{report}, which is included in the Ledger distribution. The only requirement is that you add either @option{--amount-data (-j)} or @option{--total-data (-J)} to your @command{register} command, in order to plot either the amount or total column, respectively. @node The @command{print} command, , The @command{register} command, Primary Financial Reports @subsection The @command{print} command @findex print The @command{print} command prints out ledger transactions in a textual format that can be parsed by Ledger. They will be properly formatted, and output in the most economic form possible. The @command{print} command also takes a list of optional regexes, which will cause only those postings which match in some way to be printed. The @command{print} command can be a handy way to clean up a ledger file whose formatting has gotten out of hand. @node Reports in other Formats, Reports about your Journals, Primary Financial Reports, Reporting Commands @section Reports in other Formats @menu * Comma Separated Values files:: * The @command{lisp} command:: * Emacs @command{org} Mode:: * Org mode with Babel:: * The @command{pricemap} command:: * The @command{xml} command:: * @command{prices} and @command{pricedb} commands:: @end menu @node Comma Separated Values files, The @command{lisp} command, Reports in other Formats, Reports in other Formats @subsection Comma Separated Values files @menu * The @command{csv} command:: * The @command{convert} command:: @end menu @node The @command{csv} command, The @command{convert} command, Comma Separated Values files, Comma Separated Values files @subsubsection The @command{csv} command @findex csv The @command{csv} command prints the desired ledger transactions in a csv format suitable for importing into other programs. You can specify the transactions to print using all the normal limiting and searching functions. @node The @command{convert} command, , The @command{csv} command, Comma Separated Values files @subsubsection The @command{convert} command @cindex csv importing @cindex comma separated variable file reading @findex convert @findex --input-date-format @var{DATE_FORMAT} The @command{convert} command parses a comma separated value (csv) file and prints Ledger transactions. Many banks offer csv file downloads. Unfortunately, the file formats, aside from the commas, are all different. The ledger @command{convert} command tries to help as much as it can. Your bank's csv files will have fields in different orders from other banks, so there must be a way to tell Ledger what to expect. Insert a line at the beginning of the csv file that describes the fields to Ledger. For example, this is a portion of a csv file downloaded from a credit union in the United States: @smallexample Account Name: VALUFIRST CHECKING Account Number: 71 Date Range: 11/13/2011 - 12/13/2011 Transaction Number,Date,Description,Memo,Amount Debit,Amount Credit,Balance,Check Number,Fees 767406,12/13/2011,"Deposit","CASH DEPOSIT",,45.00,00001646.89,, 767718,12/13/2011,"Withdrawal","ACE HARDWARE 16335 S HOUGHTON RD",8.80,,00001640.04,, 767406,12/13/2011,"Withdrawal","ACE HARDWARE 16335 S HOUGHTON RD",1.03,,00001648.84,, 683342,12/13/2011,"Visa Checking","NetFlix Date 12/12/11 000326585896 5968",21.85,,00001649.87,, 639668,12/13/2011,"Withdrawal","ID: 1741472662 CO: XXAA.COM PAYMNT",236.65,,00001671.72,, 1113648,12/12/2011,"Withdrawal","Tuscan IT #00037657",29.73,,00001908.37,, @end smallexample Unfortunately, as it stands Ledger cannot read it, but you can. Ledger expects the first line to contain a description of the fields on each line of the file. The fields ledger can recognize contain these case-insensitive strings @code{date}, @code{posted}, @code{code}, @code{payee} or @code{desc} or @code{description}, @code{amount} or @code{credit}, @code{debit}, @code{cost}, @code{total}, and @code{note}. Delete the account description lines at the top, and replace the first line in the data above with: @smallexample ,date,payee,note,debit,credit,,code, @end smallexample Then execute ledger like this: @smallexample $ ledger convert download.csv --input-date-format "%m/%d/%Y" @end smallexample Where the @option{--input-date-format @var{DATE_FORMAT}} option tells ledger how to interpret the dates. Importing csv files is a lot of work, but is very amenable to scripting. If your csv has only one amount column with opposite signs for credits and debits, this is also supported. For example, the first fiew lines of the above account could also be in the following format: @smallexample ,date,payee,note,credit,,code, 767406,12/13/2011,"Deposit","CASH DEPOSIT",45.00,00001646.89,, 767718,12/13/2011,"Withdrawal","ACE HARDWARE 16335 S HOUGHTON RD",-8.80,00001640.04,, @end smallexample If there are columns in the bank data you would like to keep in your ledger data, besides the primary fields described above, you can name them in the field descriptor list and Ledger will include them in the transaction as meta data if it doesn't recognize the field name. For example, if you want to capture the bank transaction number and it occurs in the first column of the data use: @smallexample transid,date,payee,note,debit,credit,,code, @end smallexample Ledger will include @samp{; transid: 767718} in the first transaction from the file above. @findex --invert @findex --auto-match @findex --account @var{STR} @findex --rich-data The @command{convert} command accepts four options. They are @option{--invert} which inverts the amount, @option{--auto-match} which automatically matches an account from the Ledger journal for every CSV line, @option{--account @var{STR}} which you can use to specify the account to balance against, and @option{--rich-data} which stores additional tag/value pairs. Using the two first lines of the above csv file, @smallexample @c file:01B0350 ,date,payee,note,debit,credit,balance,code, 767406,12/13/2011,"Deposit","CASH DEPOSIT",,45.00,00001646.89,, 767718,12/13/2011,"Withdrawal","ACE HARDWARE 16335 S HOUGHTON RD",8.80,,00001640.04,, @end smallexample and launching the below command, @smallexample @c command:01B0350,with_file:download.csv $ ledger convert download.csv --input-date-format "%m/%d/%Y" \ --invert --account Assets:MyBank --rich-data \ --file sample.dat --now=2012/01/13 @end smallexample you will get the result: @smallexample @c output:01B0350 2011/12/13 * Deposit ;CASH DEPOSIT ; balance: 00001646.89 ; CSV: 767406,12/13/2011,"Deposit","CASH DEPOSIT",,45.00,00001646.89,, ; Imported: 2012/01/13 ; UUID: ce0b7d42b02ce5eaf0d828c3b1028041fd09494c Expenses:Unknown -45 Assets:MyBank 2011/12/13 * Withdrawal ;ACE HARDWARE 16335 S HOUGHTON RD ; balance: 00001640.04 ; CSV: 767718,12/13/2011,"Withdrawal","ACE HARDWARE 16335 S HOUGHTON RD",8.80,,00001640.04,, ; Imported: 2012/01/13 ; UUID: 0aaf85911adc447ea2d5377ff6a60d6b2940047f Expenses:Unknown 8.8 Assets:MyBank @end smallexample The three added metadata are: @samp{CSV} as the original line from csv file, @samp{Imported} as the date when the csv file was imported into Ledger, and @samp{UUID} as a checksum of original csv line. If an entry with the same @samp{UUID} tag is already included in the normal ledger file (specified via @option{--file @var{FILE} (-f)} or via the environment variable @env{LEDGER_FILE}) this entry will not be printed again. In the output above, the account is @samp{Expenses:Unknown} for CSV lines. You can use the @option{--auto-match} option to automatically match an account from your Ledger journal. You can also use @command{convert} with @code{payee} and @code{account} directives. First, you can use the @code{payee} and @code{alias} directive to rewrite the @code{payee} field based on some rules. Then you can use the account and its @code{payee} directive to specify the account. I use it like this, for example: @smallexample @c input:validate payee Aldi alias ^ALDI SUED SAGT DANKE account Aufwand:Einkauf:Lebensmittel payee ^(Aldi|Alnatura|Kaufland|REWE)$ @end smallexample Note that it may be necessary for the output of @samp{ledger convert} to be passed through @code{ledger print} a second time if you want to match on the new payee field. During the @code{ledger convert} run, only the original payee name as specified in the csv data seems to be used. @node The @command{lisp} command, Emacs @command{org} Mode, Comma Separated Values files, Reports in other Formats @subsection The @command{lisp} command @findex lisp @findex emacs The @command{lisp} command prints results in a form that can be read directly by Emacs Lisp. The format of the @code{sexp} is: @smallexample ((BEG-POS CLEARED DATE CODE PAYEE (ACCOUNT AMOUNT)...) ; list of postings ...) ; list of transactions @end smallexample @noindent @command{emacs} can also be used as a synonym for @command{lisp}. @node Emacs @command{org} Mode, Org mode with Babel, The @command{lisp} command, Reports in other Formats @subsection Emacs @command{org} Mode @findex org Org mode has a sub-system known as Babel which allows for literate programming. This allows you to mix text and code within the same document and automatically execute code which may generate results which will then appear in the text. One of the languages supported by Babel is Ledger, so that you can have ledger commands embedded in a text file and have the output of ledger commands also appear in the text file. The output can be updated whenever any new ledger entries are added. For instance, the following Org mode text document snippet illustrates a very naive but still useful application of the Babel system: @smallexample * A simple test of ledger in an org file The following are some entries and I have requested that ledger be run to generate a balance on the accounts. I could have asked for a register or, in fact, anything at all the ledger can do through command-line options. #+begin_src ledger :cmdline bal :results value 2010/01/01 * Starting balance assets:bank:savings £1300.00 income:starting balances 2010/07/22 * Got paid assets:bank:chequing £1000.00 income:salary 2010/07/23 Rent expenses:rent £500.00 assets:bank:chequing #+end_src #+results: : £1800.00 assets:bank : £500.00 chequing : £1300.00 savings : £500.00 expenses:rent : £-2300.00 income : £-1000.00 salary : £-1300.00 starting balances @end smallexample Typing @kbd{C-c C-c} anywhere in the ``ledger source code block'' will invoke ledger on the contents of that block and generate a ``results'' block. The results block can appear anywhere in the file but, by default, will appear immediately below the source code block. You can combine multiple source code blocks before executing ledger and do all kinds of other wonderful things with Babel (and Org mode). @node Org mode with Babel, The @command{pricemap} command, Emacs @command{org} Mode, Reports in other Formats @subsection Org mode with Babel Using Babel, it is possible to record financial transactions conveniently in an org file and subsequently generate the financial reports required. As of Org mode 7.01, Ledger support is provided. Check the @uref{https://orgmode.org/worg/org-contrib/babel/, Babel documentation on Worg} for instructions on how to achieve this but I currently do this directly as follows: @smallexample (org-babel-do-load-languages 'org-babel-load-languages '((ledger . t) ;this is the important one for this tutorial )) @end smallexample Once Ledger support in Babel has been enabled, we can proceed to include Ledger entries within an org file. There are three ways (at least) in which these can be included: @enumerate @item place all Ledger entries within one single source block and execute this block with different arguments to generate the appropriate reports, @item place Ledger entries in more than one source block and use the @code{noweb} literary programming approach, supported by Babel, to combine these into one block elsewhere in the file for processing by Ledger, @item place Ledger entries in different source blocks and use @code{tangle} to generate a Ledger file which you can subsequently process using Ledger directly. @end enumerate The first two are described in more detail in this short tutorial. @menu * Embedded Ledger example with single source block:: * Multiple Ledger source blocks with @code{noweb}:: * Income Entries:: * Expenses:: * Financial Summaries:: * An overall balance summary:: * Generating a monthly register:: * Summary:: @end menu @node Embedded Ledger example with single source block, Multiple Ledger source blocks with @code{noweb}, Org mode with Babel, Org mode with Babel @subsubsection Embedded Ledger example with single source block The easiest, albeit possibly least useful, way in which to use Ledger within an org file is to use a single source block to record all Ledger entries. The following is an example source block: @smallexample #+name: allinone #+begin_src ledger 2010/01/01 * Starting balance assets:bank:savings £1300.00 income:starting balances 2010/07/22 * Got paid assets:bank:chequing £1000.00 income:salary 2010/07/23 Rent expenses:rent £500.00 assets:bank:chequing 2010/07/24 Food expenses:food £150.00 assets:bank:chequing 2010/07/31 * Interest on bank savings assets:bank:savings £3.53 income:interest 2010/07/31 * Transfer savings assets:bank:savings £250.00 assets:bank:chequing 2010/08/01 got paid again assets:bank:chequing £1000.00 income:salary #+end_src @end smallexample In this example, we have combined both expenses and income into one set of Ledger entries. We can now generate register and balance reports (as well as many other types of reports) using Babel to invoke Ledger with specific arguments. The arguments are passed to Ledger using the @code{:cmdline} header argument. In the code block above, there is no such argument so the system takes the default. For Ledger code blocks, the default @code{:cmdline} argument is @code{bal} and the result of evaluating this code block (@kbd{C-c C-c}) would be: @smallexample #+results: allinone() : £2653.53 assets:bank : £1100.00 chequing : £1553.53 savings : £650.00 expenses : £150.00 food : £500.00 rent : £-3303.53 income : £-3.53 interest : £-2000.00 salary : £-1300.00 starting balances @end smallexample If, instead, you wished to generate a register of all the transactions, you would change the @code{#+begin_src} line for the code block to include the required command-line option: @smallexample #+begin_src ledger :cmdline reg @end smallexample Evaluating the code block again would generate a different report. Having to change the actual directive on the code block and re-evaluate makes it difficult to have more than one view of your transactions and financial state. Eventually, Babel will support passing arguments to @code{#+call} evaluations of code blocks but this support is missing currently. Instead, we can use the concepts of literary programming, as implemented by the @code{noweb} features of Babel, to help us. @node Multiple Ledger source blocks with @code{noweb}, Income Entries, Embedded Ledger example with single source block, Org mode with Babel @subsubsection Multiple Ledger source blocks with @code{noweb} The @code{noweb} feature of Babel allows us to expand references to other code blocks within a code block. For Ledger, this can be used to group transactions according to type, say, and then bring various sets of transactions together to generate reports. Using the same transactions used above, we could consider splitting these into expenses and income, as follows: @node Income Entries, Expenses, Multiple Ledger source blocks with @code{noweb}, Org mode with Babel @subsubsection Income Entries The first set of entries relates to income, either monthly pay or interest, all typically going into one of my bank accounts. Here, I have placed several entries, but we could have had each entry in a separate @code{src} block. Note that all code blocks you wish to refer to later must have the @code{:noweb yes} header argument specified. @smallexample #+name: income #+begin_src ledger :noweb yes 2010/01/01 * Starting balance assets:bank:savings £1300.00 income:starting balances 2010/07/22 * Got paid assets:bank:chequing £1000.00 income:salary 2010/07/31 * Interest on bank savings assets:bank:savings £3.53 income:interest 2010/07/31 * Transfer savings assets:bank:savings £250.00 assets:bank:chequing 2010/08/01 got paid again assets:bank:chequing £1000.00 income:salary #+end_src @end smallexample @node Expenses, Financial Summaries, Income Entries, Org mode with Babel @subsubsection Expenses The following entries relate to personal expenses, such as rent and food. Again, these have all been placed in a single @code{src} block but could have been done individually. @smallexample #+name: expenses #+begin_src ledger :noweb yes 2010/07/23 Rent expenses:rent £500.00 assets:bank:chequing 2010/07/24 Food expenses:food £150.00 assets:bank:chequing #+end_src @end smallexample @node Financial Summaries, An overall balance summary, Expenses, Org mode with Babel @subsubsection Financial Summaries Given the ledger entries defined above in the income and expenses code blocks, we can now refer to these using the noweb expansion directives, @code{<<name>>}. We can now define different code blocks to generate specific reports for those transactions. Below are two examples, one to generate a balance report and one to generate a register report of all transactions. @node An overall balance summary, Generating a monthly register, Financial Summaries, Org mode with Babel @subsubsection An overall balance summary @findex --subtotal The overall balance of your account and expenditure with a breakdown according to category is specified by passing the @code{:cmdline bal} argument to Ledger. This code block can now be evaluated (@kbd{C-c C-c}) and the results generated by incorporating the transactions referred to by the @code{<<income>>} and @code{<<expenses>>} lines. @smallexample #+name: balance #+begin_src ledger :cmdline bal :noweb yes <<income>> <<expenses>> #+end_src #+results: balance : £2653.53 assets:bank : £1100.00 chequing : £1553.53 savings : £650.00 expenses : £150.00 food : £500.00 rent : £-3303.53 income : £-3.53 interest : £-2000.00 salary : £-1300.00 starting balances @end smallexample If you want a less detailed breakdown of where your money is, you can specify the @option{--collapse (-n)} flag (i.e. @samp{:cmdline -n bal}) to tell Ledger to exclude sub-accounts in the report. @smallexample #+begin_src ledger :cmdline -n bal :noweb yes <<income>> <<expenses>> #+end_src #+results: : £2653.53 assets : £650.00 expenses : £-3303.53 income @end smallexample @node Generating a monthly register, Summary, An overall balance summary, Org mode with Babel @subsubsection Generating a monthly register @findex register @findex --monthly You can also generate a monthly register (the @command{reg} command) by executing the following @code{src} block. This presents a summary of transactions for each monthly period (the @option{--monthly (-M)} argument) with a running total in the final column (which should be 0 at the end if all the entries are correct). @smallexample #+name: monthlyregister #+begin_src ledger :cmdline -M reg :noweb yes <<income>> <<expenses>> #+end_src #+results: monthlyregister :2010/01/01 - 2010/01/31 assets:bank:savings £1300.00 £1300.00 : in:starting balances £-1300.00 0 :2010/07/01 - 2010/07/31 assets:bank:chequing £100.00 £100.00 : assets:bank:savings £253.53 £353.53 : expenses:food £150.00 £503.53 : expenses:rent £500.00 £1003.53 : income:interest £-3.53 £1000.00 : income:salary £-1000.00 0 :2010/08/01 - 2010/08/01 assets:bank:chequing £1000.00 £1000.00 : income:salary £-1000.00 0 @end smallexample We could also generate a monthly report on our assets showing how these are increasing (or decreasing!). In this case, the final column will be the running total of the assets in our ledger. @smallexample #+name: monthlyassetsregister #+begin_src ledger :cmdline -M reg assets :noweb yes <<income>> <<expenses>> #+end_src #+results: monthlyassetsregister : 2010/01/01 - 2010/01/31 assets:bank:savings £1300.00 £1300.00 : 2010/07/01 - 2010/07/31 assets:bank:chequing £100.00 £1400.00 : assets:bank:savings £253.53 £1653.53 : 2010/08/01 - 2010/08/01 assets:bank:chequing £1000.00 £2653.53 @end smallexample @node Summary, , Generating a monthly register, Org mode with Babel @subsubsection Summary This short tutorial shows how Ledger entries can be embedded in an org file and manipulated using Babel. However, only simple Ledger features have been illustrated; please refer to the Ledger documentation for examples of more complex operations on a ledger. @node The @command{pricemap} command, The @command{xml} command, Org mode with Babel, Reports in other Formats @subsection The @command{pricemap} command @findex pricemap If you have the @file{graphviz} graph visualization package installed, ledger can generate a graph of the relationship between your various commodities. The output file is in the ``dot'' format. This is probably not very interesting, unless you have many different commodities valued in terms of each other. For example, multiple currencies and multiple investments valued in those currencies. @node The @command{xml} command, @command{prices} and @command{pricedb} commands, The @command{pricemap} command, Reports in other Formats @subsection The @command{xml} command @findex xml By default, Ledger uses a human-readable data format, and displays its reports in a manner meant to be read on screen. For the purpose of writing tools which use Ledger, however, it is possible to read and display data using XML. This section documents that format. The general format used for Ledger data is: @smallexample <?xml version="1.0"?> <ledger> <xact>...</xact> <xact>...</xact> <xact>...</xact>... </ledger> @end smallexample The data stream is enclosed in a @code{ledger} tag, which contains a series of one or more transactions. Each @code{xact} describes one transaction and contains a series of one or more postings: @smallexample <xact> <en:date>2004/03/01</en:date> <en:cleared/> <en:code>100</en:code> <en:payee>John Wiegley</en:payee> <en:postings> <posting>...</posting> <posting>...</posting> <posting>...</posting>... </en:postings> </xact> @end smallexample The date format for @code{en:date} is always @code{YYYY/MM/DD}. The @code{en:cleared} tag is optional, and indicates whether the posting has been cleared or not. There is also an @code{en:pending} tag, for marking pending postings. The @code{en:code} and @code{en:payee} tags both contain whatever text the user wishes. After the initial transaction data, there must follow a set of postings marked with @code{en:postings}. Typically these postings will all balance each other, but if not they will be automatically balanced into an account named @samp{Unknown}. Within the @code{en:postings} tag is a series of one or more @code{posting}'s, which have the following form: @smallexample <posting> <tr:account>Expenses:Computer:Hardware</tr:account> <tr:amount> <value type="amount"> <amount> <commodity flags="PT">$</commodity> <quantity>90.00</quantity> </amount> </value> </tr:amount> </posting> @end smallexample This is a basic posting. It may also begin with @code{tr:virtual} and/or @code{tr:generated} tags, to indicate virtual and auto-generated postings. Then follows the @code{tr:account} tag, which contains the full name of the account the posting is related to. Colons separate parent from child in an account name. Lastly follows the amount of the posting, indicated by @code{tr:amount}. Within this tag is a @code{value} tag, of which there are four different kinds, each with its own format: @enumerate @item Boolean, @item integer, @item amount, @item balance. @end enumerate The format of a Boolean value is @code{true} or @code{false} surrounded by a @code{boolean} tag, for example: @smallexample <boolean>true</boolean> @end smallexample The format of an integer value is the numerical value surrounded by an @code{integer} tag, for example: @smallexample <integer>12036</integer> @end smallexample The format of an amount contains two members, the commodity and the quantity. The commodity can have a set of flags that indicate how to display it. The meaning of the flags (all of which are optional) are: @table @code @item P The commodity is prefixed to the value. @item S The commodity is separated from the value by a space. @item T Thousands markers are used to display the amount. @item E The format of the amount is European, with period used as a thousands marker, and comma used as the decimal point. @end table The actual quantity for an amount is an integer of arbitrary size. Ledger uses the GNU multiple precision arithmetic library to handle such values. The XML format assumes the reader to be equally capable. Here is an example amount: @smallexample <value type="amount"> <amount> <commodity flags="PT">$</commodity> <quantity>90.00</quantity> </amount> </value> @end smallexample Lastly, a balance value contains a series of amounts, each with a different commodity. Unlike the name, such a value does need to balance. It is called a balance because it sums several amounts. For example: @smallexample <value type="balance"> <balance> <amount> <commodity flags="PT">$</commodity> <quantity>90.00</quantity> </amount> <amount> <commodity flags="TE">DM</commodity> <quantity>200.00</quantity> </amount> </balance> </value> @end smallexample That is the extent of the XML data format used by Ledger. It will output such data if the @command{xml} command is used, and can read the same data. @node @command{prices} and @command{pricedb} commands, , The @command{xml} command, Reports in other Formats @subsection @command{prices} and @command{pricedb} commands @findex prices @findex pricedb @findex --average The @command{prices} command displays the price history for matching commodities. The @option{--average (-A)} option is useful with this report, to display the running average price, or @option{--deviation (-D)} to show each price's deviation from that average. There is also a @command{pricedb} command which outputs the same information as @command{prices}, but does so in a format that can be parsed by Ledger. This is useful for generating and tidying up pricedb database files. @node Reports about your Journals, , Reports in other Formats, Reporting Commands @section Reports about your Journals @findex --count @menu * @command{accounts}:: * @command{payees}:: * @command{commodities}:: * @command{tags}:: * @command{xact}:: * @command{stats}:: * @command{select}:: @end menu @node @command{accounts}, @command{payees}, Reports about your Journals, Reports about your Journals @subsection @command{accounts} @findex accounts The @command{accounts} command reports all of the accounts in the journal. Following the command with a regular expression will limit the output to accounts matching the regex. The output is sorted by name. Using the @option{--count} option will tell you how many entries use each account. @node @command{payees}, @command{commodities}, @command{accounts}, Reports about your Journals @subsection @command{payees} @findex payees The @command{payees} command reports all of the unique payees in the journal. Using the @option{--count} option will tell you how many entries use each payee. To filter the payees displayed you must use the prefix @@: @smallexample @c command:validate $ ledger payees @@Nic @end smallexample @smallexample Nicolas Nicolas BOILABUS Oudtshoorn Municipality Vaca Veronica @end smallexample @node @command{commodities}, @command{tags}, @command{payees}, Reports about your Journals @subsection @command{commodities} @findex commodities Report all commodities present in the journals under consideration. The output is sorted by name. Using the @option{--count} option will tell you how many entries use each commodity. @node @command{tags}, @command{xact}, @command{commodities}, Reports about your Journals @subsection @command{tags} @findex tags @findex --values The @command{tags} command reports all of the tags in the journal. The output is sorted by name. Using the @option{--count} option will tell you how many entries use each tag. Using the @option{--values} option will report the values used by each tag. @node @command{xact}, @command{stats}, @command{tags}, Reports about your Journals @subsection @command{xact} @findex draft @findex entry @findex xact The @command{xact} command simplifies the creation of new transactions. It works on the principle that 80% of all postings are variants of earlier postings. Here's how it works: Say you currently have this posting in your ledger file: @smallexample @c input:03ACB97 2004/03/15 * Viva Italiano Expenses:Food $12.45 Expenses:Tips $2.55 Liabilities:MasterCard $-15.00 @end smallexample Now it's @samp{2004/4/9}, and you've just eaten at @samp{Viva Italiano} again. The exact amounts are different, but the overall form is the same. With the @command{xact} command you can type: @smallexample @c command:03ACB97 $ ledger xact 2004/4/9 viva food 11 tips 2.50 @end smallexample This produces the following output: @smallexample @c output:03ACB97 2004/04/09 Viva Italiano Expenses:Food $11.00 Expenses:Tips $2.50 Liabilities:MasterCard @end smallexample It works by finding a past posting matching the regular expression @samp{viva}, and assuming that any accounts or amounts specified will be similar to that earlier posting. If Ledger does not succeed in generating a new transaction, an error is printed and the exit code is set to @samp{1}. Here are a few more examples of the @command{xact} command, assuming the above journal transaction: @smallexample $ ledger xact 4/9 viva 11.50 $ ledger xact 4/9 viva 11.50 checking # (from `checking') $ ledger xact 4/9 viva food 11.50 tips 8 $ ledger xact 4/9 viva food 11.50 tips 8 cash $ ledger xact 4/9 viva food $11.50 tips $8 cash $ ledger xact 4/9 viva dining "DM 11.50" @end smallexample @command{draft} and @command{entry} are both synonyms of @command{xact}. @command{entry} is provided for backwards compatibility with Ledger 2.X. @node @command{stats}, @command{select}, @command{xact}, Reports about your Journals @subsection @command{stats} @findex stats @findex stat @value{FIXME:UNDOCUMENTED} @node @command{select}, , @command{stats}, Reports about your Journals @subsection @command{select} @findex select @value{FIXME:UNDOCUMENTED} @node Command-Line Syntax, Budgeting and Forecasting, Reporting Commands, Top @chapter Command-Line Syntax @menu * Basic Usage:: * Command-Line Quick Reference:: * Detailed Option Description:: * Period Expressions:: @end menu @node Basic Usage, Command-Line Quick Reference, Command-Line Syntax, Command-Line Syntax @section Basic Usage This chapter describes Ledger's features and options. You may wish to survey this to get an overview before diving into the @ref{Ledger Tutorial} and more detailed examples that follow. Ledger has a very simple command-line interface, named---enticingly enough---@file{ledger}. It supports a few reporting commands, and a large number of options for refining the output from those commands. The basic syntax of any ledger command is: @smallexample $ ledger [OPTIONS...] COMMAND [ARGS...] @end smallexample After the command word there may appear any number of arguments. For most commands, these arguments are regular expressions that cause the output to relate only to postings matching those regular expressions. For the @command{xact} command, the arguments have a special meaning, described below. The regular expressions arguments always match the account name that a posting refers to. To match on the payee of the transaction instead, precede the regular expression with @samp{payee} or @samp{@@}. For example, the following balance command reports account totals for rent, food and movies, but only those whose payee matches Freddie: @smallexample @c command:validate $ ledger bal rent food movies payee freddie @end smallexample @noindent or @smallexample @c command:validate $ ledger bal rent food movies @@freddie @end smallexample There are many, many command options available with the @file{ledger} program, and it takes a while to master them. However, none of them are required to use the basic reporting commands. @node Command-Line Quick Reference, Detailed Option Description, Basic Usage, Command-Line Syntax @section Command-Line Quick Reference @menu * Basic Reporting Commands:: * Basic Options:: * Report Filtering:: * Error Checking and Calculation Options:: * Output Customization:: * Grouping Options:: * Commodity Reporting:: @end menu @node Basic Reporting Commands, Basic Options, Command-Line Quick Reference, Command-Line Quick Reference @subsection Basic Reporting Commands @ftable @command @item balance @itemx bal Show account balances. @item register @itemx reg Show all transactions with running total. @item csv @cindex csv exporting Show transactions in csv format, for exporting to other programs. @item print Print transactions in a format readable by ledger. @item xml Produce XML output of the register command. @item lisp @itemx emacs Produce s-expression output, suitable for Emacs. @item equity Print account balances as transactions. @item prices Print price history for matching commodities. @item pricedb Print price history for matching commodities in a format readable by ledger. @item xact Generate transactions based on previous postings. @end ftable @node Basic Options, Report Filtering, Basic Reporting Commands, Command-Line Quick Reference @subsection Basic Options @ftable @option @item --help @itemx -h Display the man page for @file{ledger}. @item --version Print version information and exit. @item --file @var{FILE} @itemx -f @var{FILE} Read @file{FILE} as a ledger file. @item --output @var{FILE} @itemx -o @var{FILE} Redirect output to @file{FILE}. @item --init-file @var{FILE} @itemx -i @var{FILE} Specify an options file. @item --import @var{FILE} Import @var{FILE} as Python module. @item --account @var{STR} @itemx -a @var{STR} Specify default account @var{STR} for QIF file postings. @end ftable @node Report Filtering, Error Checking and Calculation Options, Basic Options, Command-Line Quick Reference @subsection Report Filtering @ftable @option @item --current @itemx -c Display only transactions on or before the current date. @item --begin @var{DATE} @itemx -b @var{DATE} Limit the processing to transactions on or after @var{DATE}. @item --end @var{DATE} @itemx -e @var{DATE} Limit the processing to transactions before @var{DATE}. @item --period @var{PERIOD_EXPRESSION} @itemx -p @var{PERIOD_EXPRESSION} Limit the processing to transactions in @var{PERIOD_EXPRESSION} (see @ref{Period Expressions}). @item --period-sort @var{VEXPR} Sort postings within each period according to @var{VEXPR}. @item --cleared @itemx -C Display only cleared postings. @item --dc Display register or balance in debit/credit format. @item --uncleared @itemx -U Display only uncleared postings. @item --real @itemx -R Display only real postings. @item --actual @itemx -L Display only actual postings, not automated ones. @item --related @itemx -r Display related postings. @item --budget Display how close your postings meet your budget. @item --add-budget Show unbudgeted postings. @item --unbudgeted Show only unbudgeted postings. @item --forecast-while @var{VEXPR} @itemx --forecast @var{VEXPR} Project balances into the future. @item --limit @var{EXPR} @itemx -l @var{EXPR} Limit which postings are used in calculations by @var{EXPR}. @item --amount @var{EXPR} @itemx -t @var{EXPR} Change value expression reported in @command{register} report. @item --total @var{VEXPR} @itemx -T @var{VEXPR} Change the value expression used for ``totals'' column in @command{register} and @command{balance} reports. @end ftable @node Error Checking and Calculation Options, Output Customization, Report Filtering, Command-Line Quick Reference @subsection Error Checking and Calculation Options @ftable @option @item --strict Accounts, tags or commodities not previously declared will cause warnings. @item --pedantic Accounts, tags or commodities not previously declared will cause errors. @item --check-payees Enable strict and pedantic checking for payees as well as accounts, commodities and tags. This only works in conjunction with @option{--strict} or @option{--pedantic}. @item --immediate Instruct ledger to evaluate calculations immediately rather than lazily. @end ftable @node Output Customization, Grouping Options, Error Checking and Calculation Options, Command-Line Quick Reference @subsection Output Customization @ftable @option @item --collapse @itemx -n Collapse transactions with multiple postings. @item --subtotal @itemx -s Report register as a single subtotal. @item --by-payee @itemx -P Report subtotals by payee. @item --empty @itemx -E Include empty accounts in the report. @item --weekly @itemx -W Report posting totals by week. @item --quarterly Report posting totals by quarter. @item --yearly @itemx -Y Report posting totals by year. @item --dow Report posting totals by day of week. @item --sort @var{VEXPR} @itemx -S @var{VEXPR} Sort a report using @var{VEXPR}. @item --wide @itemx -w Assume 132 columns instead of 80. @item --head @var{INT} Report the first @var{INT} postings. @item --tail @var{INT} Report the last @var{INT} postings. @item --pager @var{FILE} Direct output to @var{FILE} pager program. @item --no-pager Direct output to stdout, avoiding pager program. @item --average @itemx -A Report the average posting value. @item --deviation @itemx -D Report each posting's deviation from the average. @item --percent @itemx -% Show subtotals in the balance report as percentages. @c @item --totals @c Include running total in the @command{xml} report @item --pivot @var{TAG} Produce a pivot table of the @var{TAG} type specified. @item --amount-data @itemx -j Show only the date and value columns to format the output for plots. @item --plot-amount-format @var{FORMAT_STRING} Specify the format for the plot output. @item --total-data @itemx -J Show only the date and total columns to format the output for plots. @item --plot-total-format @var{FORMAT_STRING} Specify the format for the plot output. @item --display @var{EXPR} @itemx -d @var{EXPR} Display only postings that meet the criteria in the @var{EXPR}. @item --date-format @var{DATE_FORMAT} @itemx -y @var{DATE_FORMAT} Change the basic date format used in reports. @item --format @var{FORMAT_STRING} @itemx --balance-format @var{FORMAT_STRING} @itemx --register-format @var{FORMAT_STRING} @itemx --prices-format @var{FORMAT_STRING} @itemx -F @var{FORMAT_STRING} Set the reporting format for various reports. @item --anon Print the ledger register with anonymized accounts and payees, useful for filing bug reports. @end ftable @node Grouping Options, Commodity Reporting, Output Customization, Command-Line Quick Reference @subsection Grouping Options @ftable @option @item --by-payee @itemx -P Group postings by common payee names. @item --daily @itemx -D Group postings by day. @item --weekly @itemx -W Group postings by week. @item --monthly @itemx -M Group postings by month. @item --quarterly Group postings by quarter. @item --yearly @itemx -Y Group postings by year. @item --dow Group by day of weeks. @item --subtotal @itemx -s Group postings together, similar to the balance report. @end ftable @node Commodity Reporting, , Grouping Options, Command-Line Quick Reference @subsection Commodity Reporting @ftable @option @item --price-db @var{FILE} Use @file{FILE} for retrieving stored commodity prices. @item --price-exp @var{INT} @itemx --leeway @var{INT} @itemx -Z @var{INT} Set expected freshness of prices in @var{INT} minutes. @item --download @itemx -Q Download quotes using the script named @file{getquote}. @c FIXME: The option doesn't exist currently. @c @item --getquote @var{FILE} @c Sets the path to a user-defined script to download commodity prices. @item --quantity @itemx -O Report commodity totals without conversion. @item --basis @itemx -B Report cost basis. @item --market @itemx -V Report last known market value. @item --gain @itemx -G Report net gain or loss for commodities that have a price history. @end ftable @node Detailed Option Description, Period Expressions, Command-Line Quick Reference, Command-Line Syntax @section Detailed Option Description @menu * Global Options:: * Session Options:: * Report Options:: * Basic options:: * Report filtering:: * Output customization:: * Commodity reporting:: * Environment variables:: @end menu @node Global Options, Session Options, Detailed Option Description, Detailed Option Description @subsection Global Options Options for Ledger reports affect three separate scopes of operation: Global, Session, and Report. In practice there is very little difference between these scopes. Ledger 3.0 contains provisions for GUIs, which would make use of the different scopes by keeping an instance of Ledger running in the background and running multiple sessions with multiple reports per session. @ftable @option @item --args-only Ignore all environment and init-file settings and use only command-line arguments to control Ledger. Useful for debugging or testing small journal files not associated with your main financial database. @item --debug @var{CODE} @value{FIXME:UNDOCUMENTED} If ledger has been built with debug options this will provide extra data during the run. @item --help @itemx -h Display the man page for @file{ledger}. @item --init-file @var{FILE} Specify the location of the init file. By default, @file{$XDG_CONFIG_HOME}, @file{~/.config/ledger/lederrc}, @file{~/.ledgerrc} and @file{./.ledgerrc} are tried in order. @item --options Display the options in effect for this Ledger invocation, along with their values and the source of those values, for example: @smallexample @c command:A9349E4,with_input:03ACB97 $ ledger --options bal --cleared @end smallexample @smallexample @c output:A9349E4 =============================================================================== [Global scope options] --args-only --args-only [Session scope options] --file = A9349E4.dat --file [Report scope options] --cleared --cleared --columns = 80 --columns --limit = cleared --cleared =============================================================================== $15.00 Expenses $12.45 Food $2.55 Tips $-15.00 Liabilities:MasterCard -------------------- 0 @end smallexample @noindent For the source column, a value starting with a @samp{-} or @samp{--} indicated the source was a command-line argument. If the entry starts with a @samp{$}, the source was an environment variable. If the source is @code{?normalize} the value was set internally by ledger, in a function called @code{normalize_options}. @item --script @var{FILE} Execute a ledger script. @item --trace @var{INT} Enable tracing. The @var{INT} specifies the level of trace desired. @item --verbose @itemx -v Print detailed information on the execution of Ledger. @item --verify Enable additional assertions during run-time. This causes a significant slowdown. When combined with @option{--debug @var{CODE}} ledger will produce memory trace information. @item --verify-memory Verify that every constructed object is properly destructed. This is for debugging purposes only. @item --version Print version information and exit. @end ftable @node Session Options, Report Options, Global Options, Detailed Option Description @subsection Session Options Options for Ledger reports affect three separate scopes of operation: Global, Session, and Report. In practice there is very little difference between these scopes. Ledger 3.0 contains provisions for GUIs, which would make use of the different scopes by keeping an instance of Ledger running in the background and running multiple sessions with multiple reports per session. @ftable @option @item --check-payees Enable strict and pedantic checking for payees as well as accounts, commodities and tags. This only works in conjunction with @option{--strict} or @option{--pedantic}. @item --day-break Break up @command{register} report of @ref{timelog} entries that span multiple days by day. @c see test/baseline/opt-day-break.dat @c @smallexample @c input: @c i 2015/ @c @end smallexample @c @smallexample @c command: @c $ ledger reg --day-break @c @end smallexample @c @smallexample @c output: @c @end smallexample @item --decimal-comma Direct Ledger to parse journals using the European standard comma as a decimal separator, not the usual period. @item --download @itemx -Q Direct Ledger to download prices. @c using the script defined via the option @c @option{--getquote @var{FILE}}. @item --file @var{FILE} @itemx -f @var{FILE} Specify the input @file{FILE} for this invocation. @c FIXME: The option doesn't exist currently. @c @item --getquote @var{FILE} @c @cindex getquote @c @cindex download prices @c Tell ledger where to find the user defined script to download prices @c information. @item --input-date-format @var{DATE_FORMAT} Specify the input date format for journal entries. For example, @smallexample $ ledger convert Export.csv --input-date-format "%m/%d/%Y" @end smallexample Would convert the @file{Export.csv} file to ledger format, assuming the dates in the CSV file are like 12/23/2009 (@pxref{Date and Time Format Codes}). @item --master-account @var{STR} Prepend all account names with the argument. @smallexample @c command:A76BB56 $ ledger -f drewr3.dat bal --no-total --master-account HUMBUG @end smallexample @smallexample @c output:A76BB56 0 HUMBUG $ -3,804.00 Assets $ 1,396.00 Checking $ 30.00 Business $ -5,200.00 Savings $ -1,000.00 Equity:Opening Balances $ 6,654.00 Expenses $ 5,500.00 Auto $ 20.00 Books $ 300.00 Escrow $ 334.00 Food:Groceries $ 500.00 Interest:Mortgage $ -2,030.00 Income $ -2,000.00 Salary $ -30.00 Sales $ 180.00 Liabilities $ -20.00 MasterCard $ 200.00 Mortgage:Principal @end smallexample @item --no-aliases Ledger does not expand any aliases if this option is specified. @item --pedantic Accounts, tags or commodities not previously declared will cause errors. @item --permissive Quiet balance assertions. @item --price-db @var{FILE} Specify the location of the price entry data file. @item --price-exp @var{INT} @itemx --leeway @var{INT} @itemx -Z @var{INT} Set the expected freshness of price quotes, in @var{INT} minutes. That is, if the last known quote for any commodity is older than this value, and if @option{--download} is being used, then the Internet will be consulted again for a newer price. Otherwise, the old price is still considered to be fresh enough. @item --strict Ledger normally silently accepts any account or commodity in a posting, even if you have misspelled a commonly used one. The option @option{--strict} changes that behavior. While running with @option{--strict}, Ledger interprets all cleared transactions as correct, and if it encounters a new account or commodity (same as a misspelled commodity or account) it will issue a warning giving you the file and line number of the problem. @item --recursive-aliases Normally, ledger only expands aliases once. With this option, ledger tries to expand the result of alias expansion recursively, until no more expansions apply. @item --time-colon The @option{--time-colon} option will display the value for a seconds based commodity as real hours and minutes. For example 8100 seconds by default will be displayed as 2.25 whereas with the @option{--time-colon} option they will be displayed as 2:15. @item --value-expr @var{VEXPR} Set a global value expression annotation. @c needs example @end ftable @node Report Options, Basic options, Session Options, Detailed Option Description @subsection Report Options Options for Ledger reports affect three separate scopes of operation: Global, Session, and Report. In practice there is very little difference between these scopes. Ledger 3.0 contains provisions for GUIs, which would make use of the different scopes by keeping an instance of Ledger running in the background and running multiple sessions with multiple reports per session. @ftable @option @item --abbrev-len @var{INT} Set the minimum length an account can be abbreviated to if it doesn't fit inside the @code{account-width}. If @var{INT} is zero, then the account name will be truncated on the right. If @var{INT} is greater than @code{account-width} then the account will be truncated on the left, with no shortening of the account names in order to fit into the desired width. @item --account @var{STR} Prepend @var{STR} to all accounts reported. That is, the option @samp{--account Personal} would tack @samp{Personal:} to the beginning of every account reported in a balance report or register report. @item --account-width @var{INT} Set the width of the account column in the @command{register} report to @var{INT} characters. @item --actual @itemx -L Report only real transactions, ignoring all automated or virtual transactions. @item --add-budget Show only unbudgeted postings. @item --amount @var{EXPR} @itemx -t @var{EXPR} Apply the given value expression to the posting amount (@pxref{Value Expressions}). Using @option{--amount @var{EXPR}} you can apply an arbitrary transformation to the postings. @item --amount-data @itemx -j On a register report print only the date and amount of postings. Useful for graphing and spreadsheet applications. @item --amount-width @var{INT} Set the width in characters of the amount column in the @command{register} report. @item --anon Anonymize registry output, mostly for sending in bug reports. @item --auto-match When generating a ledger transaction from a CSV file using the @command{convert} command, automatically match an account from the Ledger journal. @item --aux-date @itemx --effective Show auxiliary dates for all calculations (@pxref{Effective Dates}). @item --average @itemx -A Print average values over the number of transactions instead of running totals. @item --average-lot-prices Report the average price at which each commodity was purchased in a balance report. @item --balance-format @var{FORMAT_STRING} Specify the format to use for the @command{balance} report (@pxref{Format Strings}). The default is: @smallexample "%(justify(scrub(display_total), 20, -1, true, color))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" "%$1\n%/" "--------------------\n" @end smallexample @item --base Reduce convertible commodities down the bottom of the conversion, e.g. display time in seconds. This also applies to custom commodity conversions (@pxref{Commodity equivalences}). @item --basis @itemx -B @itemx --cost Report the cost basis on all posting. @item --begin @var{DATE} Specify the start @var{DATE} of all calculations. Transactions before that date will be ignored. @item --bold-if @var{VEXPR} Print the entire line in bold if the given value expression is true (@pxref{Value Expressions}). @smallexample @c command:validate $ ledger reg Expenses --begin Dec --bold-if "amount>100" @end smallexample @noindent list all transactions since the beginning of December and print in bold any posting greater than $100. @item --budget Only display budgeted items. In a register report this displays transactions in the budget, in a balance report this displays accounts in the budget (@pxref{Budgeting and Forecasting}). @item --budget-format @var{FORMAT_STRING} Specify the format to use for the @command{budget} report (@pxref{Format Strings}). The default is: @smallexample "%(justify(scrub(get_at(display_total, 0)), 12, -1, true, color))" " %(justify(-scrub(get_at(display_total, 1)), 12, " " 12 + 1 + 12, true, color))" " %(justify(scrub(get_at(display_total, 1) + " " get_at(display_total, 0)), 12, " " 12 + 1 + 12 + 1 + 12, true, color))" " %(ansify_if(" " justify((get_at(display_total, 1) ? " " (100% * quantity(scrub(get_at(display_total, 0)))) / " " -quantity(scrub(get_at(display_total, 1))) : 0), " " 5, -1, true, false)," " magenta if (color and get_at(display_total, 1) and " " (abs(quantity(scrub(get_at(display_total, 0))) / " " quantity(scrub(get_at(display_total, 1)))) >= 1))))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n" "%/%$1 %$2 %$3 %$4\n%/" "%(prepend_width ? \" \" * int(prepend_width) : \"\")" "------------ ------------ ------------ -----\n" @end smallexample @item --by-payee @itemx -P Group the register report by payee. @item --cleared @itemx -C Consider only transactions that have been cleared for display and calculation. @item --cleared-format @var{FORMAT_STRING} @c FIXME thdox: to keep? Specify the format to use for the @command{cleared} report (@pxref{Format Strings}). The default is: @smallexample "%(justify(scrub(get_at(total_expr, 0)), 16, 16 + prepend_width, " " true, color)) %(justify(scrub(get_at(total_expr, 1)), 18, " " 36 + prepend_width, true, color))" " %(latest_cleared ? format_date(latest_cleared) : \" \")" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" "%$1 %$2 %$3\n%/" "%(prepend_width ? \" \" * prepend_width : \"\")" "---------------- ---------------- ---------\n" @end smallexample @item --collapse @itemx -n By default ledger prints all accounts in an account tree. With @option{--collapse} it prints only the top level account specified. @item --collapse-if-zero Collapse the account display only if it has a zero balance. @item --color @itemx --ansi Use color if the terminal supports it. @item --columns @var{INT} Specify the width of the @command{register} report in characters. @item --count Direct ledger to report the number of items when appended to the @command{commodities}, @command{accounts} or @command{payees} command. @item --csv-format @var{FORMAT_STRING} Specify the format to use for the @command{csv} report (@pxref{Format Strings}). The default is: @smallexample "%(quoted(date))," "%(quoted(code))," "%(quoted(payee))," "%(quoted(display_account))," "%(quoted(commodity(scrub(display_amount))))," "%(quoted(quantity(scrub(display_amount))))," "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," "%(quoted(join(note | xact.note)))\n" @end smallexample @item --current Shorthand for @samp{--limit "date <= today"}. @item --daily @itemx -D Shorthand for @samp{--period "daily"}. @item --date @var{EXPR} Transform the date of the transaction using @var{EXPR}. @item --date-format @var{DATE_FORMAT} @itemx -y @var{DATE_FORMAT} Specify the format ledger should use to read and print dates (@pxref{Date and Time Format Codes}). @item --date-width @var{INT} Specify the width, in characters, of the date column in the @command{register} report. @item --datetime-format @var{DATETIME_FORMAT} Specify the format ledger should use to print datetimes. @item --dc Display register or balance in debit/credit format If you use @option{--dc} with either the @command{register} (reg) or @command{balance} (bal) commands, you will now get extra columns. The register goes from this: @smallexample 12-Mar-10 Employer Assets:Cash $100 $100 Income:Employer $-100 0 12-Mar-10 KFC Expenses:Food $20 $20 Assets:Cash $-20 0 12-Mar-10 KFC - Rebate Assets:Cash $5 $5 Expenses:Food $-5 0 12-Mar-10 KFC - Food & Reb.. Expenses:Food $20 $20 Expenses:Food $-5 $15 Assets:Cash $-15 0 @end smallexample @noindent To this: @smallexample 12-Mar-10 Employer Assets:Cash $100 0 $100 In:Employer 0 $100 0 12-Mar-10 KFC Expens:Food $20 0 $20 Assets:Cash 0 $20 0 12-Mar-10 KFC - Rebate Assets:Cash $5 0 $5 Expens:Food 0 $5 0 12-Mar-10 KFC - Food &.. Expens:Food $20 0 $20 Expens:Food 0 $5 $15 Assets:Cash 0 $15 0 @end smallexample @noindent Where the first column is debits, the second is credits, and the third is the running total. Only the running total may contain negative values. For the balance report without @option{--dc}: @smallexample $70 Assets:Cash $30 Expenses:Food $-100 Income:Employer -------------------- 0 @end smallexample @noindent And with @option{--dc} it becomes this: @smallexample $105 $35 $70 Assets:Cash $40 $10 $30 Expenses:Food 0 $100 $-100 Income:Employer -------------------------------------------- $145 $145 0 @end smallexample @item --depth @var{INT} Limit the depth of displayed accounts in balance and register reports. Any accounts of greater depth are folded into their parent at the specified level. For example with @samp{--depth 2} the account @samp{Expenses:Entertainment} would be folded into @samp{Expenses:Entertainment:Dining} for display. Importantly, this is a display predicate, which means it only affects display, not the total calculations. @item --deviation Report each posting’s deviation from the average. It is only meaningful in the register and prices reports. @item --display @var{EXPR} Display only lines that satisfy the expression @var{EXPR}. @item --display-amount @var{EXPR} Apply a transformation to the @emph{displayed} amount. This happens after calculations occur. @item --display-total @var{EXPR} Apply a transformation to the @emph{displayed} total. This happens after calculations occur. @item --dow @itemx --days-of-week Group transactions by the day of the week. @smallexample @c command:validate $ ledger reg Expenses --dow --collapse @end smallexample @noindent Will print all Expenses totaled for each day of the week. @item --empty @itemx -E Include empty accounts in the report and in average calculations. @item --end @var{DATE} Specify the end @var{DATE} for a transaction to be considered in the report. All transactions on or after this date are ignored. @item --equity Related to the @command{equity} command (@pxref{The @command{equity} command}). Gives current account balances in the form of a register report. @item --exact Report beginning and ending of periods by the date of the first and last posting occurring in that period. @item --exchange "@var{COMMODITY} [, @var{COMMODITY}, ...]" @itemx -X "@var{COMMODITY} [, @var{COMMODITY}, ...]" Display values in terms of the given @var{COMMODITY}. If multiple commodities are given, values in a listed commodity will remain as-is, and others will be displayed in the first listed commodity they can be converted to. @smallexample $ ledger balance assets @end smallexample @smallexample 100 EUR 100 PHP 100 USD Assets 100 EUR EUR Bank 100 PHP PHP Bank 100 USD USD Bank -------------------- 100 EUR 100 PHP 100 USD @end smallexample @smallexample $ ledger balance assets --exchange PHP @end smallexample @smallexample 11382 PHP Assets 5801 PHP EUR Bank 100 PHP PHP Bank 5481 PHP USD Bank -------------------- 11382 PHP @end smallexample @smallexample $ ledger balance assets --exchange "PHP, EUR" @end smallexample @smallexample 100 EUR 5581 PHP Assets 100 EUR EUR Bank 100 PHP PHP Bank 5481 PHP USD Bank -------------------- 100 EUR 5581 PHP @end smallexample The latest available price is used. The syntax @option{-X @var{COMMODITY1}:@var{COMMODITY2}} displays values in @var{COMMODITY1} in terms of @var{COMMODITY2} using the latest available price, but will not automatically convert any other commodities to @var{COMMODITY2}. Multiple @option{-X} arguments may be used on a single command-line (as in @option{-X COMMODITY1:COMMODITY2 -X COMMODITY3:COMMODITY2}), which is particularly useful for situations where many prices are available for reporting in terms of @var{COMMODITY2}, but only a few should be displayed that way. @item --flat Force the full names of accounts to be used in the balance report. The balance report will not use an indented tree. @item --force-color Output TTY color codes even if the TTY doesn't support them. Useful for TTYs that don't advertise their capabilities correctly. @item --force-pager Force Ledger to paginate its output. @item --forecast-while @var{VEXPR} @itemx --forecast @var{VEXPR} Continue forecasting while @var{VEXPR} is true. @item --forecast-years @var{INT} Forecast at most @var{INT} years into the future. @item --format @var{FORMAT_STRING} @itemx -F @var{FORMAT_STRING} Use the given format string to print output. @item --gain @itemx -G @itemx --change Report on gains using the latest available prices. @item --generated Include auto-generated postings (such as those from automated transactions) in the report, in cases where you normally wouldn't want them. @item --group-by @var{EXPR} Group transactions together in the @command{register} report. @var{EXPR} can be anything, although most common would be @code{payee} or @code{commodity}. The @code{tags()} function is also useful here. @item --group-title-format @var{FORMAT_STRING} Set the format for the headers that separates the report sections of a grouped report. Only has an effect with a @option{--group-by @var{EXPR}} register report. @smallexample @c command:validate $ ledger reg Expenses --group-by "payee" --group-title-format "------------------------ %-20(value) ---------------------\n" @end smallexample @smallexample ------------------------ 7-Eleven --------------------- 2011/08/13 7-Eleven Expenses:Auto:Misc $ 5.80 $ 5.80 ------------------------ AAA Dues --------------------- 2011/06/02 AAA Dues Expenses:Auto:Misc $ 215.00 $ 215.00 ------------------------ ABC Towing and Wrecking --------------------- 2011/03/17 ABC Towing and Wrec.. Expenses:Auto:Hobbies $ 48.20 $ 48.20 ... @end smallexample @item --head @var{INT} @itemx --first @var{INT} Print the first @var{INT} entries. Opposite of @option{--tail @var{INT}}. @item --historical @itemx -H Value commodities at the time of their acquisition. @item --immediate Evaluate calculations immediately rather than lazily. @item --inject Use @code{Expected} amounts in calculations. In case you know what amount a transaction should be, but the actual transaction has the wrong value you can use metadata to specify the expected amount: @smallexample @c input:validate 2012-03-12 Paycheck Income $-990; Expected:: $-1000.00 Checking @end smallexample Then using the command @code{ledger reg --inject=Expected Income} would treat the transaction as if the ``Expected Value'' was actual. @item --invert Change the sign of all reported values. @item --limit @var{EXPR} @itemx -l @var{EXPR} Only transactions that satisfy @var{EXPR} are considered in calculations and for display. @item --lot-dates Report the date on which each commodity in a balance report was purchased. @item --lot-notes @itemx --lot-tags Report the tag attached to each commodity in a balance report. @item --lot-prices Report the price at which each commodity in a balance report was purchased. @item --lots Report the date and price at which each commodity was purchased in a balance report. @item --lots-actual Preserve the uniqueness of commodities so they aren't merged during reporting without printing the lot annotations. @item --market @itemx -V Use the latest market value for all commodities. @item --meta @var{TAG} In the register report, prepend the transaction with the value of the given @var{TAG}. @item --meta-width @var{INT} Specify the width of the Meta column used for the @option{--meta @var{TAG}} options. @item --monthly @itemx -M Synonym for @samp{--period "monthly"}. @item --no-aliases Aliases are completely ignored. @item --no-color Suppress any color TTY output. @item --no-pager Direct output to stdout, avoiding pager program. @item --no-revalued Stop Ledger from showing @code{<Revalued>} postings. This option is useful in combination with the @option{--exchange} or @option{--market} option. @item --no-rounding Don't output @samp{<Adjustment>} postings. Note that this will cause the running total to often not add up! Its main use is for @option{--amount-data (-j)} and @option{--total-data (-J)} reports. @item --no-titles Suppress the output of group titles. @item --no-total Suppress printing the final total line in a balance report. @item --now @var{DATE} Define the current date in case you want to calculate in the past or future using @option{--current}. @item --only @var{FIXME} This is a postings predicate that applies after certain transforms have been executed, such as periodic gathering. @item --output @var{FILE} Redirect the output of ledger to the file defined in @file{FILE}. @item --pager @var{FILE} Direct output to @var{FILE} pager program. @item --payee @var{VEXPR} Sets a value expression for formatting the payee. In the @command{register} report this prevents the second entry from having a date and payee for each transaction. @item --payee-width @var{INT} Set the number of columns dedicated to the payee in the register report to @var{INT}. @item --pending Use only postings that are marked pending. @item --percent @itemx -% Calculate the percentage value of each account in balance reports. Only works for accounts that have a single commodity. @item --period @var{PERIOD_EXPRESSION} Define a period expression that sets the time period during which transactions are to be accounted. For a @command{register} report only the transactions that satisfy the period expression with be displayed. For a @command{balance} report only those transactions will be accounted in the final balances. @item --pivot @var{TAG} Produce a balance pivot report @emph{around} the given @var{TAG}. For example, if you have multiple cars and track each fuel purchase in @samp{Expenses:Auto:Fuel} and tag each fuel purchase with a tag identifying which car the purchase was for @samp{; Car: Prius}, then the command: @smallexample @c command:validate $ ledger bal Fuel --pivot "Car" --period "this year" @end smallexample @smallexample $ 3491.26 Car $ 1084.22 M3:Expenses:Auto:Fuel $ 149.65 MG V11:Expenses:Auto:Fuel $ 621.89 Prius:Expenses:Auto:Fuel $ 1635.50 Sienna:Expenses:Auto:Fuel $ 42.69 Expenses:Auto:Fuel -------------------- $ 3533.95 @end smallexample @xref{Metadata values}. @item --plot-amount-format @var{FORMAT_STRING} Define the output format for an amount data plot. @xref{Visualizing with Gnuplot}. @item --plot-total-format @var{FORMAT_STRING} Define the output format for a total data plot. @xref{Visualizing with Gnuplot}. @item --prepend-format @var{FORMAT_STRING} Prepend @var{STR} to every line of the output. @item --prepend-width @var{INT} Reserve @var{INT} spaces at the beginning of each line of the output. @item --price @itemx -I Use the price of the commodity purchase for performing calculations. @item --pricedb-format @var{FORMAT_STRING} Set the format expected for the historical price file. @item --prices-format @var{FORMAT_STRING} Set the format for the @command{prices} report. @item --primary-date @itemx --actual-dates Show primary dates for all calculations (@pxref{Effective Dates}). @item --quantity @itemx -O Report commodity totals (this is the default). @item --quarterly Synonym for @samp{--period "quarterly"}. @item --raw In the @command{print} report, show transactions using the exact same syntax as specified by the user in their data file. Don't do any massaging or interpreting. This can be useful for minor cleanups, like just aligning amounts. @item --real @itemx -R Account using only real transactions ignoring virtual and automatic transactions. @item --register-format @var{FORMAT_STRING} Define the output format for the @command{register} report. @item --related In a @command{register} report show the related account. This is the other @emph{side} of the transaction. @item --related-all Show all postings in a transaction, similar to @option{--related} but show both @emph{sides} of each transaction. @item --revalued Report discrepancy in values for manual reports by inserting @code{<Revalued>} postings. This is implied when using the @option{--exchange} or @option{--market} option. @item --revalued-only Show only @code{<Revalued>} postings. @item --revalued-total @var{FIXME} Display the sum of the revalued postings as the running total, which serves to show unrealized capital in a gain/losses report. @item --rich-data @itemx --detail When generating a ledger transaction from a CSV file using the @command{convert} command, add CSV, Imported, and UUID metadata. @item --seed @var{INT} Set the random seed to @var{INT} for the @code{generate} command. Used as part of development testing. @item --sort @var{VEXPR} @itemx -S @var{VEXPR} Sort the @command{register} report based on the value expression given to sort. @item --sort-all @var{FIXME} @value{FIXME:UNDOCUMENTED} @item --sort-xacts @var{VEXPR} @itemx --period-sort @var{VEXPR} Sort the postings within transactions using the given value expression. @item --start-of-week @var{INT} Tell ledger to use a particular day of the week to start its ``weekly'' summary. @samp{--start-of-week=1} specifies Monday as the start of the week. @item --subtotal @itemx -s Cause all transactions in a @command{register} report to be collapsed into a single, subtotaled transaction. @item --tail @var{INT} @itemx --last @var{INT} Report only the last @var{INT} entries. Only useful in a @command{register} report. @item --time-report Add two columns to the balance report to show the earliest checkin and checkout times for timelog entries. @item --total @var{VEXPR} @itemx -T @var{VEXPR} Define a value expression used to calculate the total in reports. @item --total-data @itemx -J Show only dates and totals to format the output for plots. @item --total-width @var{INT} Set the width of the total field in the register report. @item --truncate @var{CODE} Indicates how truncation should happen when the contents of columns exceed their width. Valid arguments are @samp{leading}, @samp{middle}, and @samp{trailing}. The default is smarter than any of these three, as it considers sub-names within the account name (that style is called ``abbreviate''). @item --unbudgeted Show only unbudgeted postings. @item --uncleared @itemx -U Use only uncleared transactions in calculations and reports. @item --unrealized Show generated unrealized gain and loss accounts in the balance report. @item --unrealized-gains @var{STR} Allow the user to specify what account name should be used for unrealized gains. Defaults to @samp{"Equity:Unrealized Gains"}. Often set in one's init file to change the default. @item --unrealized-losses @var{STR} Allow the user to specify what account name should be used for unrealized losses. Defaults to @samp{"Equity:Unrealized Losses"}. Often set in one's init file to change the default. @item --unround Perform all calculations without rounding and display results to full precision. @item --values Shows the values used by each tag when used in combination with the @command{tags} command. @item --weekly @itemx -W Synonym for @samp{--period "weekly"}. @item --wide Let the register report use 132 columns instead of 80 (the default). Identical to @samp{--columns "132"}. @item --yearly @itemx -Y Synonym for @samp{--period "yearly"}. @end ftable @node Basic options, Report filtering, Report Options, Detailed Option Description @subsection Basic options These are the most basic command options. Most likely, the user will want to set them using environment variables (see @ref{Environment variables}), instead of using actual command-line options: @ftable @option @item --help @itemx -h Display the man page for @file{ledger}. @item --version Print the current version of ledger and exits. This is useful for sending bug reports, to let the author know which version of ledger you are using. @item --file @var{FILE} @itemx -f @var{FILE} Read @file{FILE} as a ledger file. @var{FILE} can be @samp{-} which is a synonym for @samp{/dev/stdin}. This command may be used multiple times. Typically, the environment variable @env{LEDGER_FILE} is set, rather than using this command-line option. @item --output @var{FILE} @itemx -o @var{FILE} Redirect output from any command to @file{FILE}. By default, all output goes to standard output. @item --init-file @var{FILE} @itemx -i @var{FILE} Causes @file{FILE} to be read by ledger before any other ledger file. This file may not contain any postings, but it may contain option settings. To specify options in the init file, use the same syntax as on the command-line, but put each option on its own line. Here is an example init file: @smallexample @c input:validate --price-db ~/finance/.pricedb --wide ; ~/.ledgerrc ends here @end smallexample Option settings on the command-line or in the environment always take precedence over settings in the init file. @item --account @var{STR} @itemx -a @var{STR} Specify the default account which QIF file postings are assumed to relate to. @end ftable @node Report filtering, Output customization, Basic options, Detailed Option Description @subsection Report filtering These options change which postings affect the outcome of a report, in ways other than just using regular expressions: @ftable @option @item --current @itemx -c Display only transactions occurring on or before the current date. @item --begin @var{DATE} @itemx -b @var{DATE} Constrain the report to transactions on or after @var{DATE}. Only transactions after that date will be calculated, which means that the running total in the balance report will always start at zero with the first matching transaction. (Note: This is different from using @option{--display @var{EXPR}} to constrain what is displayed). @item --end @var{DATE} @itemx -e @var{DATE} Constrain the report so that transactions on or after @var{DATE} are not considered. @item --period @var{PERIOD_EXPRESSION} @itemx -p @var{PERIOD_EXPRESSION} Set the reporting period to @var{STR}. This will subtotal all matching transactions within each period separately, making it easy to see weekly, monthly, quarterly, etc., posting totals. A period string can even specify the beginning and end of the report range, using simple terms like @samp{last June} or @samp{next month}. For more details on period expressions, see @ref{Period Expressions}. @item --period-sort @var{VEXPR} Sort the postings within each reporting period using the value expression @var{EXPR}. This is most often useful when reporting monthly expenses, in order to view the highest expense categories at the top of each month: @c TODO: the parameter to --period-sort was -At, which doesn't seem to work any longer @smallexample @c command:validate $ ledger -M --period-sort total reg ^Expenses @end smallexample @item --cleared @itemx -C Display only postings whose transaction has been marked ``cleared'' (by placing an asterisk to the right of the date). @item --uncleared @itemx -U Display only postings whose transaction has not been marked ``cleared'' (i.e., if there is no asterisk to the right of the date). @item --real @itemx -R Display only real postings, not virtual. (A virtual posting is indicated by surrounding the account name with parentheses or brackets; see @ref{Virtual postings} for more information). @item --actual @itemx -L Display only actual postings, and not those created by automated transactions. @item --related @itemx -r Display postings that are related to whichever postings would otherwise have matched the filtering criteria. In the register report, this shows where money went to, or the account it came from. In the balance report, it shows all the accounts affected by transactions having a related posting. For example, if a file had this transaction: @smallexample @c input:94C5675 2004/03/20 Safeway Expenses:Food $65.00 Expenses:Cash $20.00 Assets:Checking $-85.00 @end smallexample And the register command was: @smallexample @c command:94C5675 $ ledger -f example.dat -r register food @end smallexample The following would be printed, showing the postings related to the posting that matched: @smallexample @c output:94C5675 04-Mar-20 Safeway Expenses:Cash $20.00 $20.00 Assets:Checking $-85.00 $-65.00 @end smallexample @item --budget Useful for displaying how close your postings meet your budget. @option{--add-budget} also shows unbudgeted postings, while @option{--unbudgeted} shows only those. @option{--forecast @var{VEXPR}} is a related option that projects your budget into the future, showing how it will affect future balances. @xref{Budgeting and Forecasting}. @item --limit @var{EXPR} @itemx -l @var{EXPR} Limit which postings take part in the calculations of a report. @item --amount @var{EXPR} @itemx -t @var{EXPR} Change the value expression used to calculate the ``value'' column in the @command{register} report, the amount used to calculate account totals in the @command{balance} report, and the values printed in the @command{equity} report. @xref{Value Expressions}. @item --total @var{VEXPR} @itemx -T @var{VEXPR} Set the value expression used for the ``totals'' column in the @command{register} and @command{balance} reports. @end ftable @c @node Search Terms, Output Customization, Report Filtering, Detailed Options Description @c @subsection Search Terms @c Valid Ledger invocations look like: @c @smallexample @c ledger [OPTIONS] <COMMAND> <SEARCH-TERMS> @c @end smallexample @c Where @code{COMMAND} is any command verb (@pxref{Reporting @c Commands}), @code{OPTIONS} can occur anywhere, and @c @code{SEARCH-TERM} is one or more of the following: @c @smallexample @c word search for any account containing 'word' @c TERM and TERM boolean AND between terms @c TERM or TERM boolean OR between terms @c not TERM invert the meaning of the term @c payee word search for any payee containing 'word' @c @@word shorthand for 'payee word' @c desc word alternate for 'payee word' @c note word search for any note containing 'word' @c &word shorthand for 'note word' @c tag word search for any metadata tag containing 'word' @c tag word=value search for any metadata tag containing 'word' @c whose value contains 'value' @c %word shorthand for 'tag word' @c %word=value shorthand for 'tag word=value' @c meta word alternate for 'tag word' @c meta word=value alternate for 'tag word=value' @c expr 'EXPR' apply the given value expression as a predicate @c '=EXPR' shorthand for 'expr EXPR' @c \( TERMS \) group terms; useful if using and/or/not @c @end smallexample @c So, to list all transaction that charged to ``food'' but not @c ``dining'' for any payee other than ``chang'' the following three @c commands would be equivalent: @c @smallexample @c ledger reg food not dining @@chang @c ledger reg food and not dining and not payee chang @c ledger reg food not dining expr 'payee =~ /chang/' @c @end smallexample @node Output customization, Commodity reporting, Report filtering, Detailed Option Description @subsection Output customization These options affect only the output, but not which postings are used to create it: @ftable @option @item --collapse @itemx -n Cause transactions in a @command{register} report with multiple postings to be collapsed into a single, subtotaled transaction. @item --subtotal @itemx -s Cause all transactions in a @command{register} report to be collapsed into a single, subtotaled transaction. @item --by-payee @itemx -P Report subtotals by payee. @item --empty @itemx -E Include even empty accounts in the @command{balance} report. @item --weekly @itemx -W Report posting totals by the week. The week begins on whichever day of the week begins the month containing that posting. To set a specific begin date, use a period string, such as @samp{weekly from DATE}. @item --monthly @itemx -M Report posting totals by month. @item --yearly @itemx -Y Report posting totals by year. For more complex periods, use @option{--period}. @c TODO end this sentence @item --period @var{PERIOD_EXPRESSION} Option described above. @item --dow Report posting totals for each day of the week. This is an easy way to see if weekend spending is more than on weekdays. @item --sort @var{VEXPR} @itemx -S @var{VEXPR} Sort a report by comparing the values determined using the value expression @var{VEXPR}. For example, using @samp{-S "-abs(total)"} in the @command{balance} report will sort account balances from greatest to least, using the absolute value of the total. For more on how to use value expressions, see @ref{Value Expressions}. @item --pivot @var{TAG} Produce a pivot table around the @var{TAG} provided. This requires meta data using valued tags. @item --wide @itemx -w Cause the default @command{register} report to assume 132 columns instead of 80. @item --head @var{INT} Cause only the first @var{INT} transactions to be printed. This is different from using the command-line utility @file{head}, which would limit to the first @var{INT} postings. @option{--tail @var{INT}} outputs only the last @var{INT} transactions. Both options may be used simultaneously. If a negative amount is given, it will invert the meaning of the flag (instead of the first five transactions being printed, for example, it would print all but the first five). @item --pager @var{FILE} Tell Ledger to pass its output to the given @var{FILE} pager program; very useful when the output is especially long. This behavior can be made the default by setting the @env{LEDGER_PAGER} environment variable. @item --no-pager Tell Ledger to @emph{not} pass its output to a pager program; useful when a pager is set by default. @item --average @itemx -A Report the average posting value. @item --deviation @itemx -D Report each posting's deviation from the average. It is only meaningful in the @command{register} and @command{prices} reports. @item --percent @itemx -% Show account subtotals in the @command{balance} report as percentages of the parent account. @c @option{--totals} include running total information in the @c @command{xml} report. @item --amount-data @itemx -j Change the @command{register} report so that it prints nothing but the date and the value column, and the latter without commodities. This is only meaningful if the report uses a single commodity. This data can then be fed to other programs, which could plot the date, analyze it, etc. @item --total-data @itemx -J Change the @command{register} report so that it prints nothing but the date and total columns, without commodities. @item --display @var{EXPR} @itemx -d @var{EXPR} Limit which postings or accounts are actually displayed in a report. They might still be calculated, and be part of the running total of a register report, for example, but they will not be displayed. This is useful for seeing last month's checking postings, against a running balance which includes all posting values: @smallexample @c command:validate $ ledger -d "d>=[last month]" reg checking @end smallexample The output from this command is very different from the following, whose running total includes only postings from the last month onward: @smallexample @c command:validate $ ledger -p "last month" reg checking @end smallexample Which is more useful depends on what you're looking to know: the total amount for the reporting range (using @option{--period @var{PERIOD_EXPRESSION} (-p)}), or simply a display restricted to the reporting range (using @option{--display @var{EXPR} (-d)}). @item --date-format @var{DATE_FORMAT} @itemx -y @var{DATE_FORMAT} Change the basic date format used by reports. The default uses a date like @samp{2004/08/01}, which represents the default date format of @code{%Y/%m/%d}. To change the way dates are printed in general, the easiest way is to put @option{--date-format @var{DATE_FORMAT}} in the Ledger init file (or the file referred to by @env{LEDGER_INIT}). @item --format @var{FORMAT_STRING} @itemx -F @var{FORMAT_STRING} Set the reporting format for whatever report ledger is about to make. @xref{Format Strings}. There are also specific format commands for each report type: @item --balance-format @var{FORMAT_STRING} Define the output format for the @command{balance} report. The default (defined in @file{report.h} is: @smallexample "%(ansify_if( justify(scrub(display_total), 20, 20 + int(prepend_width), true, color), bold if should_bold)) %(!options.flat ? depth_spacer : \"\") %-(ansify_if( ansify_if(partial_account(options.flat), blue if color), bold if should_bold))\n%/ %$1\n%/ %(prepend_width ? \" \" * int(prepend_width) : \"\") --------------------\n" @end smallexample @item --cleared-format @var{FORMAT_STRING} Define the format for the cleared report. The default is: @smallexample "%(justify(scrub(get_at(display_total, 0)), 16, 16 + int(prepend_width), true, color)) %(justify(scrub(get_at(display_total, 1)), 18, 36 + int(prepend_width), true, color)) %(latest_cleared ? format_date(latest_cleared) : \" \") %(!options.flat ? depth_spacer : \"\") %-(ansify_if(partial_account(options.flat), blue if color))\n%/ %$1 %$2 %$3\n%/ %(prepend_width ? \" \" * int(prepend_width) : \"\") ---------------- ---------------- ---------\n" @end smallexample @item --register-format @var{FORMAT_STRING} Define the output format for the @command{register} report. The default (defined in @file{report.h} is: @smallexample "%(ansify_if( ansify_if(justify(format_date(date), int(date_width)), green if color and date > today), bold if should_bold)) %(ansify_if( ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), bold if color and !cleared and actual), bold if should_bold)) %(ansify_if( ansify_if(justify(truncated(display_account, int(account_width), int(abbrev_len)), int(account_width)), blue if color), bold if should_bold)) %(ansify_if( justify(scrub(display_amount), int(amount_width), 3 + int(meta_width) + int(date_width) + int(payee_width) + int(account_width) + int(amount_width) + int(prepend_width), true, color), bold if should_bold)) %(ansify_if( justify(scrub(display_total), int(total_width), 4 + int(meta_width) + int(date_width) + int(payee_width) + int(account_width) + int(amount_width) + int(total_width) + int(prepend_width), true, color), bold if should_bold))\n%/ %(justify(\" \", int(date_width))) %(ansify_if( justify(truncated(has_tag(\"Payee\") ? payee : \" \", int(payee_width)), int(payee_width)), bold if should_bold)) %$3 %$4 %$5\n" @end smallexample @item --csv-format @var{FORMAT_STRING} Set the format for @command{csv} reports. The default is: @smallexample "%(quoted(date)), %(quoted(code)), %(quoted(payee)), %(quoted(display_account)), %(quoted(commodity(scrub(display_amount)))), %(quoted(quantity(scrub(display_amount)))), %(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\"))), %(quoted(join(note | xact.note)))\n" @end smallexample @item --plot-amount-format @var{FORMAT_STRING} Set the format for amount plots, using the @option{--amount-data (-j)} option. The default is: @smallexample "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n" @end smallexample @item --plot-total-format @var{FORMAT_STRING} Set the format for total plots, using the @option{--total-data (-J)} option. The default is: @smallexample "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n" @end smallexample @item --pricedb-format @var{FORMAT_STRING} Set the format expected for the historical price file. The default is: @smallexample "P %(datetime) %(display_account) %(scrub(display_amount))\n" @end smallexample @item --prices-format @var{FORMAT_STRING} Set the format for the @command{prices} report. The default is: @smallexample "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, 2 + 9 + 8 + 12, true, color))\n" @end smallexample @end ftable @node Commodity reporting, Environment variables, Output customization, Detailed Option Description @subsection Commodity reporting These options affect how commodity values are displayed: @ftable @option @item --price-db @var{FILE} Set the file that is used for recording downloaded commodity prices. It is always read on startup, to determine historical prices. Other settings can be placed in this file manually, to prevent downloading quotes for a specific commodity, for example. This is done by adding a line like the following: @smallexample @c input:validate ; Don't download quotes for the dollar, or timelog values N $ N h @end smallexample @noindent Note: Ledger NEVER writes output to files. You are responsible for updating the price-db file. The best way is to have your price download script maintain this file. The format of the file can be changed by telling ledger to use the @option{--pricedb-format @var{FORMAT_STRING}} you define. @item --price-exp @var{INT} @itemx --leeway @var{INT} @itemx -Z @var{INT} Set the expected freshness of price quotes, in @var{INT} minutes. That is, if the last known quote for any commodity is older than this value, and if @option{--download} is being used, then the Internet will be consulted again for a newer price. Otherwise, the old price is still considered to be fresh enough. @item --download @itemx -Q Cause quotes to be automagically downloaded, as needed, by running a script named @file{getquote} and expecting that script to return a value understood by ledger. A sample implementation of a @file{getquote} script, implemented in Perl, is provided in the distribution. Downloaded quote price are then appended to the price database, usually specified using the environment variable @env{LEDGER_PRICE_DB}. @end ftable There are several different ways that ledger can report the totals it displays. The most flexible way to adjust them is by using value expressions, and the @option{--amount @var{EXPR} (-t)} and @option{--total @var{VEXPR} (-T)} options. However, there are also several ``default'' reports, which will satisfy most users' basic reporting needs: @ftable @option @item --quantity @itemx -O Report commodity totals (this is the default). @item --basis @itemx -B Report the cost basis for all postings. @item --market @itemx -V Use the last known value for commodities to calculate final values. @item --gain @itemx -G Report the net gain/loss for all commodities in the report that have a price history. @end ftable Often you will be more interested in the value of your entire holdings, in your preferred currency. It might be nice to know you hold 10,000 shares of PENNY, but you are more interested in whether or not that is worth $1000.00 or $10,000.00. However, the current day value of a commodity can mean different things to different people, depending on the accounts involved, the commodities, the nature of the transactions, etc. @findex --now @var{DATE} @findex --market @findex --exchange "@var{COMMODITY} [, @var{COMMODITY}, ...]" When you specify @option{--market (-V)}, or @option{--exchange @var{COMMODITY} (-X)}, you are requesting that some or all of the commodities be valuated as of today (or whatever @option{--now @var{DATE}} is set to). But what does such a valuation mean? This meaning is governed by the presence of a @var{VALUE} meta-data property, whose content is an expression used to compute that value. If no @var{VALUE} property is specified, each posting is assumed to have a default, as if you'd specified a global, automated transaction as follows: @smallexample @c input:validate = expr true ; VALUE:: market(amount, date, exchange) @end smallexample This definition emulates the present day behavior of @option{--market (-V)} and @option{--exchange @var{COMMODITY} (-X)} (in the case of @samp{-X}, the requested commodity is passed via the string @samp{exchange} above). @cindex Euro conversion One thing many people have wanted to do is to fixate the valuation of old European currencies in terms of the Euro after a certain date: @smallexample @c input:validate = expr commodity == "DM" ; VALUE:: date < [Jun 2008] ? market(amount, date, exchange) : 1.44 EUR @end smallexample This says: If @option{--now @var{DATE}} is some old date, use market prices as they were at that time; but if @option{--now @var{DATE}} is past June 2008, use a fixed price for converting Deutsche Mark to Euro. Or how about never re-valuating commodities used in Expenses, since they cannot have a different future value: @smallexample @c input:validate = /^Expenses:/ ; VALUE:: market(amount, post.date, exchange) @end smallexample This says the future valuation is the same as the valuation at the time of posting. @code{post.date} equals the posting's date, while just 'date' is the value of @option{--now @var{DATE}} (defaults to today). Or how about valuating miles based on a reimbursement rate during a specific time period: @smallexample @c input:validate = expr commodity == "miles" and date >= [2007] and date < [2008] ; VALUE:: market($1.05, date, exchange) @end smallexample In this case, miles driven in 2007 will always be valuated at $1.05 each. If you use @samp{-X EUR} to expressly request all amounts in Euro, Ledger shall convert $1.05 to Euro by whatever means are appropriate for dollars. Note that you can have a valuation expression specific to a particular posting or transaction, by overriding these general defaults using specific meta-data: @smallexample @c input:validate 2010-12-26 Example Expenses:Food $20 ; Just to be silly, always valuate *these* $20 as 30 DM, no matter what ; the user asks for with -V or -X ; VALUE:: 30 DM Assets:Cash @end smallexample This example demonstrates that your value expression should be as symbolic as possible, using terms like 'amount' and 'date', rather than specific amounts and dates. Also, you should pass the amount along to the function 'market' so it can be further revalued if the user has asked for a specific currency. Or, if it better suits your accounting, you can be less symbolic, which allows you to report most everything in EUR if you use @samp{-X EUR}, except for certain accounts or postings which should always be valuated in another currency. For example: @c TODO is this example missing the actual line to get the effect? @c it looks like it only contains a match, but no effect @smallexample @c input:validate = /^Assets:Brokerage:CAD$/ ; Always report the value of commodities in this account in ; terms of present day dollars, despite what was asked for ; on the command-line VALUE:: market(amount, date, @samp{$}) @end smallexample @cindex FIFO/LIFO @cindex LIFO/FIFO @findex --lots @findex --lot-prices @findex --exchange "@var{COMMODITY} [, @var{COMMODITY}, ...]" @findex --historical @findex --basis @findex --price Ledger presently has no way of handling such things as FIFO and LIFO. If you specify an unadorned commodity name, like AAPL, it will balance against itself. If @option{--lots} are not being displayed, then it will appear to balance against any lot of AAPL. @cindex adorned commodity @findex --lot-prices If you specify an adorned commodity, like AAPL @{$10.00@}, it will also balance against itself, and against any AAPL if @option{--lots} is not specified. But if you do specify @option{--lot-prices}, for example, then it will balance against that specific price for AAPL. Normally when you use @option{--exchange @var{COMMODITY} (-X)} to request that amounts be reported in a specific commodity, Ledger uses these values: @itemize @item Register Report For the @command{register} report, use the value of that commodity on the date of the posting being reported, with a @samp{<Revalued>} posting added at the end if today's value is different from the value of the last posting. @item Balance Report For the @command{balance} report, use the value of that commodity as of today. @end itemize You can now specify @option{--historical (-H)} to ask that all valuations for any amount be done relative to the date that amount was encountered. You can also now use @option{--exchange @var{COMMODITY} (-X)} (and @option{--historical (-H)}) in conjunction with @option{--basis (-B)} and @option{--price (-I)}, to see valuation reports of just your basis costs or lot prices. Finally, sometimes, you may seek to only report one (or some subset) of the commodities in terms of another commodity. In this situation, you can use the syntax @option{--exchange @var{COMMODITY1}:@var{COMMODITY2}} to request that ledger always display @var{COMMODITY1} in terms of @var{COMMODITY2}, but you want no other commodities to be automatically displayed in terms of @var{COMMODITY2} without additional @option{--exchange} options. For example, if you wanted to report EUR and BTC in terms of USD, but report all other commodities without conversion to USD, you could use: @option{--exchange EUR:USD --exchange BTC:USD}. @node Environment variables, , Commodity reporting, Detailed Option Description @subsection Environment variables Every option to ledger may be set using an environment variable if the option has a long name. For example setting the environment variable @samp{@env{LEDGER_DATE_FORMAT}="%d.%m.%Y"} will have the same effect as specifying @samp{@option{--date-format} '%d.%m.%Y'} on the command-line. Options on the command-line always take precedence over environment variable settings, however. Note that you may also permanently specify option values by placing option settings in the file @file{~/.ledgerrc} one option per line, for example: @smallexample @c input:validate --pager /bin/cat --date-format %d.%m.%Y @end smallexample @node Period Expressions, , Detailed Option Description, Command-Line Syntax @section Period Expressions @c TODO use @var below A period expression indicates a span of time, or a reporting interval, or both. Ledger's end dates are always exclusive, imagine the date is followed by 00:00:00 time. They are instants in time not entire days. The full syntax is: @smallexample [INTERVAL] [BEGIN] [END] @end smallexample The optional @var{INTERVAL} part may be any one of: @smallexample every day every week every month every quarter every year every N days # N is any integer every N weeks every N months every N quarters every N years daily weekly biweekly monthly bimonthly quarterly yearly @end smallexample After the interval, a begin time, end time, both or neither may be specified. As for the begin time, it can be either of: @smallexample from <SPEC> since <SPEC> @end smallexample The end time can be either of: @smallexample to <SPEC> until <SPEC> @end smallexample Where @var{SPEC} can be any of: @smallexample 2004 2004/10 2004/10/1 10/1 october oct this week # or day, month, quarter, year next week last week @end smallexample The beginning and ending can be given at the same time, if it spans a single period. In that case, just use @var{SPEC} by itself. In that case, the period @samp{oct}, for example, will cover all the days in October. The possible forms are: @smallexample <SPEC> in <SPEC> @end smallexample Here are a few examples of period expressions: @smallexample monthly monthly in 2004 weekly from oct weekly from last month from sep to oct from 10/1 to 10/5 monthly until 2005 from apr until nov last oct weekly last august @end smallexample @node Budgeting and Forecasting, Time Keeping, Command-Line Syntax, Top @chapter Budgeting and Forecasting @menu * Budgeting:: * Forecasting:: @end menu @node Budgeting, Forecasting, Budgeting and Forecasting, Budgeting and Forecasting @section Budgeting @findex --budget @findex --add-budget @findex --unbudgeted @findex --monthly Keeping a budget allows you to pay closer attention to your income and expenses, by reporting how far your actual financial activity is from your expectations. To start keeping a budget, put some periodic transactions (@pxref{Periodic Transactions}) at the top of your ledger file. A periodic transaction is almost identical to a regular transaction, except that it begins with a tilde and has a period expression in place of a payee. For example: @smallexample @c input:validate ~ Monthly Expenses:Rent $500.00 Expenses:Food $450.00 Expenses:Auto:Gas $120.00 Expenses:Insurance $150.00 Expenses:Phone $125.00 Expenses:Utilities $100.00 Expenses:Movies $50.00 Expenses $200.00 ; all other expenses Assets ~ Yearly Expenses:Auto:Repair $500.00 Assets @end smallexample These two periodic transactions give the usual monthly expenses, as well as one typical yearly expense. For help on finding out what your average monthly expenses are for any category, use a command like: @smallexample @c command:validate $ ledger -p "this year" --monthly --average register ^expenses @end smallexample The reported totals are the current year's average for each account. Once these periodic transactions are defined, creating a budget report is as easy as adding @option{--budget} to the command-line. For example, a typical monthly expense report would be: @smallexample @c command:validate $ ledger --monthly register ^expenses @end smallexample To see the same report balanced against your budget, use: @smallexample @c command:validate $ ledger --budget --monthly register ^expenses @end smallexample A budget report includes only those accounts that appear in the budget. To see all expenses balanced against the budget, use @option{--add-budget}. You can even see only the unbudgeted expenses using @option{--unbudgeted}: @smallexample @c command:validate $ ledger --unbudgeted --monthly register ^expenses @end smallexample You can also use these flags with the @command{balance} command. @node Forecasting, , Budgeting, Budgeting and Forecasting @section Forecasting @findex --forecast @var{VEXPR} Sometimes it's useful to know what your finances will look like in the future, such as determining when an account will reach zero. Ledger makes this easy to do, using the same periodic transactions as are used for budgeting. An example forecast report can be generated with: @smallexample @c command:validate $ ledger --file drewr3.dat --forecast "T>@{\$-500.00@}" register ^assets ^liabilities @end smallexample This report continues outputting postings until the running total is greater than $-500.00. A final posting is always shown, to inform you what the total afterwards would be. Forecasting can also be used with the @command{balance} report, but by date only, and not against the running total: @smallexample @c command:validate $ ledger --forecast "d<[2010]" bal ^assets ^liabilities @end smallexample @node Time Keeping, Value Expressions, Budgeting and Forecasting, Top @chapter Time Keeping @findex --day-break @anchor{timelog} Ledger directly supports ``timelog'' entries, which have this form: @smallexample @c input:validate i 2013/03/28 22:13:00 ACCOUNT[ PAYEE] o 2013/03/29 03:39:00 @end smallexample This records a check-in to the given ACCOUNT, and a check-out. You can be checked-in to multiple accounts at a time, if you wish, and they can span multiple days (use @option{--day-break} to break them up in the report). The number of seconds between check-in and check-out is accumulated as time to that ACCOUNT. If the checkout uses a capital @samp{O}, the transaction is marked ``cleared''. You can use an optional PAYEE for whatever meaning you like. Now, there are a few ways to generate this information. You can use the @file{timeclock.el} package, which is part of Emacs. Or you can write a simple script in whichever language you prefer to emit similar information. Or you can use Org mode's time-clocking abilities and the @file{org2tc} script developed by John Wiegley. These timelog entries can appear in a separate file, or directly in your main ledger file. The initial @samp{i} and @samp{o} characters count as Ledger ``directives'', and are accepted anywhere that ordinary transactions are valid. @node Value Expressions, Format Strings, Time Keeping, Top @chapter Value Expressions @findex --limit @var{EXPR} @findex --display @var{EXPR} Ledger uses value expressions to make calculations for many different purposes: @enumerate @item The values displayed in reports. @item For predicates (where truth is anything non-zero), to determine which postings are calculated (option @option{--limit @var{EXPR} (-l)}) or displayed (option @option{--display @var{EXPR} (-d)}). @item For sorting criteria, to yield the sort key. @item In the matching criteria used by automated postings. @end enumerate Value expressions support most simple math and logic operators, in addition to a set of functions and variables. @c A function's argument is whatever follows it. The following is @c a display predicate that I use with the @command{balance} command: @c @smallexample @c ledger -d '/^Liabilities/?T<0:UT>100' balance @c @end smallexample @c The effect is that account totals are displayed only if: 1) A @c Liabilities account has a total less than zero; or 2) the absolute @c value of the account's total exceeds 100 units of whatever commodity @c contains. If it contains multiple commodities, only one of them must @c exceed 100 units. Display predicates are also very handy with register reports, to constrain which transactions are printed. For example, the following command shows only transactions from the beginning of the current month, while still calculating the running balance based on all transactions: @smallexample @c command:validate $ ledger -d "d>[this month]" register checking @end smallexample The advantage of this command's complexity is that it prints the running total in terms of all transactions in the register. The following, simpler command is similar, but totals only the displayed postings: @smallexample @c command:validate $ ledger -b "this month" register checking @end smallexample @menu * Variables:: * Functions:: * Operators:: * Complex expressions:: @end menu @node Variables, Functions, Value Expressions, Value Expressions @section Variables @findex --amount @var{EXPR} @findex --total @var{VEXPR} Below are the one letter variables available in any value expression. For the @command{register} and @command{print} commands, these variables relate to individual postings, and sometimes the account affected by a posting. For the @command{balance} command, these variables relate to accounts, often with a subtle difference in meaning. The use of each variable for both is specified. @table @code @item t This maps to whatever the user specified with @option{--amount @var{EXPR} (-t)}. In a @command{register} report, @option{--amount @var{EXPR} (-t)} changes the value column; in a @command{balance} report, it has no meaning by default. If @option{--amount @var{EXPR} (-t)} was not specified, the current report style's value expression is used. @item T This maps to whatever the user specified with @option{--total @var{VEXPR} (-T)}. In a register report, @option{--total @var{VEXPR} (-T)} changes the totals column; in a balance report, this is the value given for each account. If @option{--total @var{VEXPR} (-T)} was not specified, the current report style's value expression is used. @item m This is always the present moment/date. @end table @menu * Posting/account details:: * Calculated totals:: @end menu @node Posting/account details, Calculated totals, Variables, Variables @subsection Posting/account details @table @code @item d @itemx date A posting's date, as the number of seconds past the epoch. This is always ``today'' for an account. @item aux_date A posting's aux date @item a @itemx amount The posting's amount; the balance of an account, without considering children. @item b The cost of a posting; the cost of an account, without its children. @item v The market value of a posting or an account, without its children. @item g The net gain (market value minus cost basis), for a posting or an account, without its children. It is the same as @samp{v-b}. @item depth The depth (``level'') of an account. If an account has one parent, its depth is one. @item n The index of a posting, or the count of postings affecting an account. @item X @itemx cleared @samp{1} if a posting's transaction has been cleared, @samp{0} otherwise. @item uncleared @samp{1} if a posting's transaction state is uncleared, @samp{0} otherwise. @item pending @samp{1} if a posting's transaction state is pending, @samp{0} otherwise. @item R @samp{1} if a posting is not virtual, @samp{0} otherwise. @item Z @samp{1} if a posting is not automated, @samp{0} otherwise. @end table @node Calculated totals, , Posting/account details, Variables @subsection Calculated totals @table @code @item O The total of all postings seen so far, or the total of an account and all its children. @item N The total count of postings affecting an account and all its children. @end table @node Functions, Operators, Variables, Value Expressions @section Functions The available one letter functions are: @table @code @item - Negates the argument. @item U The absolute (unsigned) value of the argument. @item S Strips the commodity from the argument. @item P The present market value of the argument. The syntax @samp{P(x,d)} is supported, which yields the market value at time @samp{d}. If no date is given, then the current moment is used. @end table @node Operators, Complex expressions, Functions, Value Expressions @section Operators The operators, in order of precedence, are: @enumerate @item @code{* /} @item @code{+ -} @item @code{! < > =} @item @code{& | ?:} @end enumerate @menu * Unary Operators:: * Binary Operators:: @end menu @node Unary Operators, Binary Operators, Operators, Operators @subsection Unary Operators @code{not} (@code{!}) @code{neg} @node Binary Operators, , Unary Operators, Operators @subsection Binary Operators @code{==} @code{<} @code{<=} @code{>} @code{>=} @code{and} @code{or} @code{+} @code{-} @code{*} @code{/} @code{QUERY} @code{COLON} @code{CONS} @code{SEQ} @code{DEFINE} @code{LOOKUP} @code{LAMBDA} @code{CALL} @code{MATCH} @node Complex expressions, , Operators, Value Expressions @section Complex expressions More complicated expressions are possible using: @table @code @item expr "amount == COMMODITY AMOUNT" The amount can be any kind of amount supported by ledger, with or without a commodity. Use this for decimal values. @item /REGEX/ @itemx expr account =~ /REGEX/ A regular expression that matches against an account's full name. If a posting, this will match against the account affected by the posting. @item @@/REGEX/ @itemx expr payee =~ /REGEX/ A regular expression that matches against a transaction's payee name. @item %/REGEX/ @itemx expr has_tag(/REGEX/) @itemx expr has_tag('TAG') A regular expression (REGEX) or string (TAG) that checks for the tags of a transaction. @item expr has_meta(/REGEX/) @itemx expr has_meta('TAG') A regular expression (REGEX) or string (TAG) that checks for the metadata key of a transaction. @item expr tag(REGEX) =~ /REGEX/ A regular expression that matches a transaction's tags against its values. @item expr date =~ /REGEX/ Useful for specifying a date in plain terms. For example, you could say @samp{expr date =~ /2014/}. @item expr comment =~ /REGEX/ A regular expression that matches against a posting's comment field. This searches only a posting's field, not the transaction's note or comment field. For example, @code{ledger reg "expr" "comment =~ /landline/"} will match: @smallexample @c input:validate 2014/1/29 Phone bill Assets:Checking $50.00 Expenses:Phone $-50.00 ; landline bill @end smallexample but will not match: @smallexample @c input:validate 2014/1/29 Phone bill ; landline bill ; landline bill Assets:Checking $50.00 Expenses:Phone $-50.00 @end smallexample To match the latter, use @samp{ledger reg "expr" "note =~ /landline/"} instead. @item expr note =~ /REGEX/ A regular expression that matches against a transaction's note field. This searches all comments in the transaction, including comments on individual postings. Thus, @samp{ledger reg "expr" "note =~ /landline/"} will match all the three examples below: @smallexample @c input:validate 2014/1/29 Phone bill Assets:Checking $50.00 Expenses:Phone $-50.00 ; landline bill @end smallexample @smallexample @c input:validate 2014/1/29 Phone bill ; landline bill Assets:Checking $50.00 Expenses:Phone $-50.00 @end smallexample @smallexample @c input:validate 2014/1/29 Phone bill ; landline bill Assets:Checking $50.00 Expenses:Phone $-50.00 @end smallexample @item (EXPR) A sub-expression is nested in parenthesis. This can be useful passing more complicated arguments to functions, or for overriding the natural precedence order of operators. @item expr base =~ /REGEX/ A regular expression that matches against an account's base name. If a posting, this will match against the account affected by the posting. @item expr code =~ /REGEX/ A regular expression that matches against the transaction code (the text that occurs between parentheses before the payee). @item expr any(KEYWORD =~ /REGEX/) The @command{any} keyword is used to specify that at least one posting of the transaction must match the expression in brackets. For example, @samp{ledger -f d reg expr "any(account =~ /Assets:/)"} can be used to display all transactions which involve at least one @samp{Assets:} account. @item expr all(KEYWORD =~ /REGEX/) The @command{all} keyword is used to specify that all postings of a transactions must match the expression in brackets. For example, @samp{ledger -f d reg expr "all(account =~ /Assets:/)"} can be used to display all transactions where all accounts are @samp{Assets:}. @end table The @command{query} command can be used to see how Ledger interprets your query. This can be useful if you are not getting the results you expect (@pxref{Pre-Commands}). @menu * Miscellaneous:: @end menu @node Miscellaneous, , Complex expressions, Complex expressions @subsection Miscellaneous The following Ledger journal data (saved as @file{expr.dat}) is used to explain the behaviour of the functions and variables below: @anchor{expr.dat} @smallexample @c input:3406FC1 2015/01/16 * (C0D3) Payee Assets:Cash ¤ -123,45 ; Payee: PiggyBank Expenses:Office Supplies @end smallexample @defun abs value @defunx U value Return the absolute value of the given @var{value}, e.g. @var{amount}. @smallexample @c command:3406FC1 $ ledger -f expr.dat --format "%(account) %(abs(amount))\n" reg assets @end smallexample @smallexample @c output:3406FC1 Assets:Cash ¤ 123,45 @end smallexample @end defun @defun amount_expr Return the calculated amount of the posting according to the @option{--amount} option. @end defun @defun ansify_if value color bool Render the given @var{expression} as a string, applying the proper ANSI escape codes to display it in the given @var{color} if @var{bool} is true. It typically checks the value of the option @option{--color}. Since ANSI escape codes include non-printable character sequences, such as escape @kbd{^[} the following example may not appear as the final result on the command-line. @smallexample @c command:4D836EE,with_input:3406FC1 $ ledger -f expr.dat --format "%(ansify_if(account, blue, options.color))\n" reg @end smallexample @smallexample @c output:4D836EE Assets:Cash Expenses:Office Supplies @end smallexample @end defun @defun ceiling value Return the next integer of @var{value} toward @math{+}infinity. @smallexample @c command:FF9C18C,with_input:3406FC1 $ ledger -f expr.dat --format "%(account) %(ceiling(amount))\n" reg @end smallexample @smallexample @c output:FF9C18C Assets:Cash ¤ -123,00 Expenses:Office Supplies ¤ 124,00 @end smallexample @end defun @defvar code Return the transaction code, the string between the parenthesis after the date. @smallexample @c command:46FCFD3,with_input:3406FC1 $ ledger -f expr.dat --format "%(account) %(code)\n" reg assets @end smallexample @smallexample @c output:46FCFD3 Assets:Cash C0D3 @end smallexample @end defvar @defvar commodity Return the commodity of the posting amount. @end defvar @smallexample @c command:2CD27D7,with_input:3406FC1 $ ledger -f expr.dat --format "%(account) %(commodity)\n" reg @end smallexample @smallexample @c output:2CD27D7 Assets:Cash ¤ Expenses:Office Supplies ¤ @end smallexample @defvar date @defvarx d Return the date of the posting. @end defvar @smallexample @c command:67EBA45,with_input:3406FC1 $ ledger -f expr.dat --format "%(date) %(account)\n" reg assets @end smallexample @smallexample @c output:67EBA45 2015/01/16 Assets:Cash @end smallexample @defvar display_amount @defvarx t @value{FIXME:UNDOCUMENTED} @end defvar @c FIXME @defvar display_total @defvarx T @value{FIXME:UNDOCUMENTED} @end defvar @defun floor value Return the next integer of @var{value} toward @math{-}infinity. @smallexample @c command:4FDC7C5,with_input:3406FC1 $ ledger -f expr.dat --format "%(account) %(floor(amount))\n" reg @end smallexample @smallexample @c output:4FDC7C5 Assets:Cash ¤ -124,00 Expenses:Office Supplies ¤ 123,00 @end smallexample @end defun @defun format string Evaluate @var{string} as format just like the @option{--format} option. @end defun @defun format_date date format Return the @var{date} as a string using @var{format}. See @code{strftime (3)} for format string details. @smallexample @c command:9605B13,with_input:3406FC1 $ ledger -f expr.dat --format "%(format_date(date, '%A, %B %d. %Y'))\n" reg assets @end smallexample @smallexample @c output:9605B13 Friday, January 16. 2015 @end smallexample @end defun @defun format_datetime datetime format Return the @var{datetime} as a string using @var{format}. Refer to @code{strftime (3)} for format string details. @end defun @defun get_at sequence index Return the value in @var{sequence} at @var{index}. The first element is @var{index} 0. @value{InternalUseOnly} @end defun @defun is_seq value Return true if @var{value} is a sequence. @value{InternalUseOnly} @end defun @defun join value Replace all newlines in @var{value} with @code{\n}. @end defun @defun justify value first_width latter_width right_justify colorize Right or left justify the string representing @var{value}. The width of the field in the first line is given by @var{first_width}. For subsequent lines the width is given by @var{latter_width}. If @var{latter_width=-1}, then @var{first_width} is used for all lines. If @var{right_justify=true} then the field is right justified within the width of the field. If it is @var{false}, then the field is left justified and padded to the full width of the field. If @var{colorize} is true, then ledger will honor color settings. @smallexample @c command:082FB27,with_input:3406FC1 $ ledger -f expr.dat --format "»%(justify(account, 30, 30, true))«\n" reg @end smallexample @smallexample @c output:082FB27 » Assets:Cash« » Expenses:Office Supplies« @end smallexample @end defun @defun market value datetime @defunx P Return the price of @var{value} at @var{datetime}. Note that @var{datetime} must be surrounded by brackets in order to be parsed correctly, e.g. @code{[2012/03/23]}. @end defun @defun nail_down @value{FIXME:UNDOCUMENTED} @end defun @defvar now @defvarx m Return the current datetime. @end defvar @defvar options A variable that allows access to the values of the given command-line options using the long option names, e.g. to see whether @option{--daily} or @option{-D} was given use @code{option.daily}. @smallexample @c command:C1FC7A7,with_input:3406FC1 $ ledger -f expr.dat -X $ -D --format "%(options.daily) %(options.exchange)\n" reg assets @end smallexample @smallexample @c output:C1FC7A7 true $ @end smallexample @end defvar @defun percent value_a value_b Return the percentage of @var{value_a} in relation to @var{value_b} (used as 100%) @smallexample @c command:04959BF,with_input:3406FC1 $ ledger -f expr.dat --format "%(percent(amount, 200))\n" reg @end smallexample @smallexample @c output:04959BF -61.73% 61.73% @end smallexample @end defun @defun print value Print @var{value} to stdout. @value{InternalUseOnly} @end defun @defun quantity value Return the quantity of @var{value} for values that have a per-unit cost. @end defun @defun quoted expression Surround @var{expression} with double quotes. If expression contains a double quote, it will be escaped with a backslash. @smallexample @c command:EAD8AA7,with_input:3406FC1 $ ledger -f expr.dat --format "%(quoted(account)) %(quoted(amount))\n" reg @end smallexample @smallexample @c output:EAD8AA7 "Assets:Cash" "¤ -123,45" "Expenses:Office Supplies" "¤ 123,45" @end smallexample @end defun @defun quoted_rfc expression Similar, except an embedded double quote would be escaped by preceding it with another double quote, as prescribed by RFC 4180. @end defun @defun round @value{FIXME:UNDOCUMENTED} @end defun @defun rounded @value{FIXME:UNDOCUMENTED} @end defun @defun roundto value n Return @var{value} rounded to @var{n} digits. Does not affect formatting. @smallexample @c command:B4DFB9F,with_input:3406FC1 $ ledger -f expr.dat --format "%(account) %(roundto(amount, 1))\n" reg @end smallexample @smallexample @c output:B4DFB9F Assets:Cash ¤ -123,40 Expenses:Office Supplies ¤ 123,50 @end smallexample @end defun @defun scrub value Clean @var{value} using various transformations such as @code{round}, stripping value annotations, and more. @end defun @defun should_bold Return true if expression given to @option{--bold-if} evaluates to true. @value{InternalUseOnly} @end defun @defun strip value @defunx S Strip value annotation from @var{value}. @end defun @defun to_amount value Convert @var{value} to an amount. @value{InternalUseOnly} @end defun @defun to_balance value Convert @var{value} to a balance. @value{InternalUseOnly} @end defun @defun to_boolean value Convert @var{value} to a boolean. @value{InternalUseOnly} @end defun @defun to_date value Convert @var{value} to a date. @value{InternalUseOnly} @end defun @defun to_datetime value Convert @var{value} to a datetime. @value{InternalUseOnly} @end defun @defun to_int value @defunx int value Return the integer value for @var{value}. @smallexample @c command:0B0CBA1,with_input:3406FC1 $ ledger -f expr.dat --format "%(1 + to_int('1'))\n%(2,5 + int(2,5))\n" reg assets @end smallexample @smallexample @c output:0B0CBA1 2 4.5 @end smallexample @end defun @defun to_mask value Convert @var{value} to a mask. @value{InternalUseOnly} @end defun @defun to_sequence value Convert @var{value} to a sequence. @value{InternalUseOnly} @end defun @defun to_string value @defunx str value Convert @var{value} to a character string. @end defun @defvar today Return today's date. @end defvar @smallexample @c command:F2FDF4B,with_input:3406FC1 $ ledger -f expr.dat --now 2015/01/01 --format "%(today)\n" reg assets @end smallexample @smallexample @c output:F2FDF4B 2015/01/01 @end smallexample @defun top_amount @value{FIXME:UNDOCUMENTED} @end defun @defun total_expr Return the calculated total of the posting according to the @option{--total} option. @end defun @defun trim value Trim leading and trailing whitespace from @var{value}. @smallexample @c command:377BBAB,with_input:3406FC1 $ ledger -f expr.dat --format "»%(trim(' Trimmed '))«\n" reg assets @end smallexample @smallexample @c output:377BBAB »Trimmed« @end smallexample @end defun @defun truncated string total_len account_len Truncate @var{string} to @var{total_len} ensuring that each account is at least @var{account_len} long. @end defun @defun unround @value{FIXME:UNDOCUMENTED} @end defun @defun unrounded @value{FIXME:UNDOCUMENTED} @end defun @defun value_date @value{FIXME:UNDOCUMENTED} @end defun @node Format Strings, Extending with Python, Value Expressions, Top @chapter Format Strings @menu * Format String Basics:: * Format String Structure:: * Format Expressions:: * Balance format:: * Formatting Functions and Codes:: @end menu @node Format String Basics, Format String Structure, Format Strings, Format Strings @section Format String Basics @findex --format @var{FORMAT_STRING} @findex --balance-format @var{FORMAT_STRING} @findex --budget-format @var{FORMAT_STRING} @findex --cleared-format @var{FORMAT_STRING} @findex --csv-format @var{FORMAT_STRING} @findex --plot-amount-format @var{FORMAT_STRING} @findex --plot-total-format @var{FORMAT_STRING} @findex --pricedb-format @var{FORMAT_STRING} @findex --prices-format @var{FORMAT_STRING} @findex --register-format @var{FORMAT_STRING} Format strings may be used to change the output format of reports. They are specified by passing a formatting string to the @option{--format @var{FORMAT_STRING} (-F)} option. Within that string, constructs are allowed which make it possible to display the various parts of an account or posting in custom ways. There are several additional flags that allow you to define formats for specific reports. These are useful to define in your configuration file and will allow you to run ledger reports from the command-line without having to enter a new format for each command. @itemize @item @option{--balance-format @var{FORMAT_STRING}} @item @option{--budget-format @var{FORMAT_STRING}} @item @option{--cleared-format @var{FORMAT_STRING}} @item @option{--csv-format @var{FORMAT_STRING}} @item @option{--plot-amount-format @var{FORMAT_STRING}} @item @option{--plot-total-format @var{FORMAT_STRING}} @item @option{--pricedb-format @var{FORMAT_STRING}} @item @option{--prices-format @var{FORMAT_STRING}} @item @option{--register-format @var{FORMAT_STRING}} @end itemize @node Format String Structure, Format Expressions, Format String Basics, Format Strings @section Format String Structure Within a format string, a substitution is specified using a percent @samp{%} character. The basic format of all substitutions is: @smallexample %[-][MIN WIDTH][.MAX WIDTH](VALEXPR) @end smallexample If the optional minus sign @samp{-} follows the percent character @samp{%}, whatever is substituted will be left justified. The default is right justified. If a minimum width is given next, the substituted text will be at least that wide, perhaps wider. If a period and a maximum width is given, the substituted text will never be wider than this, and will be truncated to fit. Here are some examples: @table @code @item %-20P A transaction's payee, left justified and padded to 20 characters wide. @item %20P The same, right justified, at least 20 chars wide. @item %.20P The same, no more than 20 chars wide. @end table The expression following the format constraints can be a single letter, or an expression enclosed in parentheses or brackets. @node Format Expressions, Balance format, Format String Structure, Format Strings @section Format Expressions @findex --amount @var{EXPR} @findex --total @var{VEXPR} For demonstration purposes the journal data from @ref{expr.dat} is used. The allowable expressions are: @table @code @item % Inserts a percent sign. @smallexample @c command:6F90EFC,with_input:3406FC1 $ ledger -f expr.dat --format "%%\n" reg assets @end smallexample @smallexample @c output:6F90EFC % @end smallexample @item t Inserts the results of the value expression specified by @option{--amount @var{EXPR} (-t)}. If @option{--amount @var{EXPR} (-t)} was not specified, the current report style's value expression is used. @item T Inserts the results of the value expression specified by @option{--total @var{VEXPR} (-T)}. If @option{--total @var{VEXPR} (-T)} was not specified, the current report style's value expression is used. @item (EXPR) Inserts the amount resulting from the value expression given in parentheses. To insert five times the total value of an account, for example, one could say @samp{%12(5*O)}. Note: It's important to put the five first in that expression, so that the commodity doesn't get stripped from the total. @smallexample @c command:494256E,with_input:3406FC1 $ ledger -f expr.dat --format "%12(5*O)\n" reg assets @end smallexample @smallexample @c output:494256E ¤ -617,25 @end smallexample @item [DATEFMT] Inserts the result of formatting a posting's date with a date format string, exactly like those supported by @code{strftime (3)}. For example: @samp{%[%Y/%m/%d %H:%M:%S]}. @item S Insert the path name of the file from which the transaction's data was read. Only sensible in a @command{register} report. @c Note: Unable to test this properly since the output depends on @c where the ledger source tree resides in the filesystem. @smallexample $ ledger -f ~/journal.dat --format "%S\n" reg assets @end smallexample @smallexample /home/jwiegley/journal.dat @end smallexample @item B Inserts the beginning character position of that transaction within the file. @smallexample @c command:2B669C9,with_input:3406FC1 $ ledger -f expr.dat --format "%B\n" reg assets @end smallexample @smallexample @c output:2B669C9 26 @end smallexample @item b Inserts the beginning line of that transaction within the file. @smallexample @c command:F6E356F,with_input:3406FC1 $ ledger -f expr.dat --format "%b\n" reg assets @end smallexample @smallexample @c output:F6E356F 2 @end smallexample @item E Inserts the ending character position of that transaction within the file. @smallexample @c command:0E55246,with_input:3406FC1 $ ledger -f expr.dat --format "%E\n" reg assets @end smallexample @smallexample @c output:0E55246 90 @end smallexample @item e Inserts the ending line of that transaction within the file. @smallexample @c command:A26F4C0,with_input:3406FC1 $ ledger -f expr.dat --format "%e\n" reg assets @end smallexample @smallexample @c output:A26F4C0 3 @end smallexample @item D Returns the date according to the default format. @item d Returns the date according to the default format. If the transaction has an effective date, it prints @code{ACTUAL_DATE=EFFECTIVE_DATE}. @item X If a posting has been cleared, this returns a 1, otherwise returns 0. @item Y This is the same as @samp{%X}, except that it only displays a state character if all of the member postings have the same state. @item C Inserts the transaction code. This is the value specified between parentheses on the first line of the transaction. @smallexample @c command:C1CAAF3,with_input:3406FC1 $ ledger -f expr.dat --format "%C\n" reg assets @end smallexample @c Note: The output needs a space character at the end @c for this test to pass @smallexample @c output:C1CAAF3 (C0D3) @end smallexample @item P Inserts the payee related to a posting. @smallexample @c command:F41A9BB,with_input:3406FC1 $ ledger -f expr.dat --format "%P\n" reg assets @end smallexample @smallexample @c output:F41A9BB PiggyBank @end smallexample @c @item a @c Inserts the optimal short name for an account. This is normally @c used in balance reports. It prints a parent account's name if that @c name has not been printed yet, otherwise it just prints the @c account's name. @item A Inserts the full name of an account. @smallexample @c command:29A70DD,with_input:3406FC1 $ ledger -f expr.dat --format "%A\n" reg @end smallexample @smallexample @c output:29A70DD Assets:Cash Expenses:Office Supplies @end smallexample @c @item W @c This is the same as @code{%A}, except that it first displays the @c posting's state @emph{if the transaction's posting states are not @c all the same}, followed by the full account name. This is offered @c as a printing optimization, so that combined with @code{%Y}, only @c the minimum amount of state detail is printed. @c @item o @c Inserts the ``optimized'' form of a posting's amount. This is used @c by the print report. In some cases, this inserts nothing; in @c others, it inserts the posting amount and its cost. It's use is @c not recommended unless you are modifying the print report. @item N Inserts the note associated with a posting, if one exists. @smallexample @c command:E6DC93A,with_input:3406FC1 $ ledger -f expr.dat --format "%N\n" reg assets @end smallexample @smallexample @c output:E6DC93A Payee: PiggyBank @end smallexample @item / The @samp{%/} construct is special. It separates a format string between what is printed for the first posting of a transaction, and what is printed for all subsequent postings. If not used, the same format string is used for all postings. @smallexample @c command:E80897D,with_input:3406FC1 $ ledger -f expr.dat --format "%P\n%/%A\n" reg @end smallexample @smallexample @c output:E80897D PiggyBank Expenses:Office Supplies @end smallexample @end table @node Balance format, Formatting Functions and Codes, Format Expressions, Format Strings @section Balance format @findex --balance-format @var{FORMAT_STRING} @findex --format @var{FORMAT_STRING} As an example of how flexible the @option{--format @var{FORMAT_STRING}} strings can be, the default balance format looks like this (the various functions are described later): @smallexample "%(justify(scrub(display_total), 20, -1, true, color))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" "%$1\n%/" "--------------------\n" @end smallexample @node Formatting Functions and Codes, , Balance format, Format Strings @section Formatting Functions and Codes @menu * Field Widths:: * Colors:: * Quantities and Calculations:: * Date Functions:: * Date and Time Format Codes:: * Text Formatting:: * Data File Parsing Information:: @end menu @node Field Widths, Colors, Formatting Functions and Codes, Formatting Functions and Codes @subsection Field Widths The following codes return the width allocated for the specific fields. The defaults can be changed using the corresponding command-line options: @itemize @item @code{date_width} @item @code{payee_width} @item @code{account_width} @item @code{amount_width} @item @code{total_width} @end itemize @node Colors, Quantities and Calculations, Field Widths, Formatting Functions and Codes @subsection Colors The character-based formatting ledger can do is limited to the ANSI terminal character colors and font highlights in a normal TTY session. @multitable @columnfractions .3 .3 .3 @item @code{red} @tab @code{magenta} @tab @code{bold} @item @code{green} @tab @code{cyan} @tab @code{underline} @item @code{yellow} @tab @code{white} @tab @code{blink} @item @code{blue} @tab @code{black} @end multitable @node Quantities and Calculations, Date Functions, Colors, Formatting Functions and Codes @subsection Quantities and Calculations @table @code @item amount_expr @item abs @item commodity @item display_amount @item display_total @item floor @item get_at @item is_seq @item market @item percent @item price @item quantity @item rounded @item truncated @item total_expr @item top_amount @item to_boolean @item to_int @item to_amount @item to_balance @item unrounded @end table @node Date Functions, Date and Time Format Codes, Quantities and Calculations, Formatting Functions and Codes @subsection Date Functions @findex --now @var{DATE} The following functions allow you to manipulate and format dates. @table @code @item date Return the date of the current transaction. @item format_date(date, "FORMAT_STRING") Format the date using the given format string. @item now Return the current date and time. If the @option{--now @var{DATE}} option is defined it will return that value. @item today Return the current date. If the @option{--now @var{DATE}} option is defined it will return that value. @item to_datetime Convert a string to a date-time value. @item to_date Convert a string to date value. @item value_date @end table @menu * Date and Time Format Codes:: @end menu @node Date and Time Format Codes, Text Formatting, Date Functions, Formatting Functions and Codes @subsection Date and Time Format Codes Date and time format are specified as strings of single letter codes preceded by percent signs. Any separator, or no separator can be specified. @menu * Days:: * Weekdays:: * Month:: * Miscellaneous Date Codes:: @end menu @node Days, Weekdays, Date and Time Format Codes, Date and Time Format Codes @subsubsection Days Dates are formed from a combination of day, month and year codes, in whatever order you prefer: @table @code @item %Y Four digit year. @item %y Two digit year. @item %m Two digit month. @item %d Two digit date. @end table @noindent So @code{"%Y%m%d"} yields @samp{20111214} which provides a date that is simple to sort on. @node Weekdays, Month, Days, Date and Time Format Codes @subsubsection Weekdays You can have additional weekday information in your date with @samp{%A} as @table @code @item %m-%d-%Y %A yields @samp{02-10-2010 Wednesday}. @item %A %m-%d-%Y yields @samp{Wednesday 02-10-2010}. @end table @noindent These are options you can select for weekday @table @code @item %a weekday, abbreviated Wed. @item %A weekday, full Wednesday. @item %d day of the month (dd), zero padded up to 10. @item %e day of the month (dd), no leading zero up to 10. @item %j day of year, zero padded 000--366. @item %u day of week starting with Monday (1), i.e. @code{mtwtfss} 3. @item %w day of week starting with Sunday (0), i.e. @code{smtwtfs} 3. @end table @node Month, Miscellaneous Date Codes, Weekdays, Date and Time Format Codes @subsubsection Month You can have additional month information in your date with @samp{%B} as @table @code @item %m-%d-%Y %B yields @samp{02-10-2010 February}. @item %B %m-%d-%Y yields @samp{February 02-10-2010}. @end table @noindent These are options you can select for month @table @code @item %m @samp{mm} month as two digits. @item %b Locale’s abbreviated month, for example @samp{02} might be abbreviated as @samp{Feb}. @item %B Locale’s full month, variable length, e.g. February. @end table @node Miscellaneous Date Codes, , Month, Date and Time Format Codes @subsubsection Miscellaneous Date Codes Additional date format parameters which can be used: @table @code @item %U week number Sunday as first day of week, ranging 01--53. @item %W week number Monday as first day of week, ranging 01--53. @item %V week of the year, ranging 01--53. @item %C century, ranging 00--99. @item %D yields @code{%m/%d/%y} as in @samp{02/10/10}. @item %x locale’s date representation, as @samp{02/10/2010} for the U.S. @item %F yields @code{%Y-%m-%d} as in @samp{2010-02-10}. @end table @node Text Formatting, Data File Parsing Information, Date and Time Format Codes, Formatting Functions and Codes @subsection Text Formatting The following format functions allow you limited formatting of text: @table @code @item ansify_if(value, color) Surrounds the string representing value with ANSI codes to give it @code{color} on a TTY display. Has no effect if directed to a file. @item justify(value, first_width, latter_width, right_justify, colorize) Right or left justify the string representing @code{value}. The width of the field in the first line is given by @code{first_width}. For subsequent lines the width is given by @code{latter_width}. If @code{latter_width=-1}, then @code{first_width} is use for all lines. If @code{right_justify=true} then the field is right justify within the width of the field. If it is @code{false}, then the field is left justified and padded to the full width of the field. If @code{colorize} is true, then ledger will honor color settings. @item join(STR) Replaces line feeds in @code{STR} with @samp{\n}. @item quoted(STR) Return @code{STR} surrounded by double quotes, @samp{"STR"}. @item strip(value) Values can have numerous annotations, such as effective dates and lot prices. @code{strip} removes these annotations. @end table @node Data File Parsing Information, , Text Formatting, Formatting Functions and Codes @subsection Data File Parsing Information The following format strings provide locational metadata regarding the coordinates of entries in the source data file(s) that generated the posting. @table @code @item filename the name of the ledger data file from whence the posting came, abbreviated @samp{S}. @item beg_pos character position in @code{filename} where entry for posting begins, abbreviated @samp{B}. @item end_pos character position in @code{filename} where entry for posting ends, abbreviated @samp{E}. @item beg_line line number in @code{filename} where entry for posting begins, abbreviated @samp{b}. @item end_line line number in @code{filename} where entry for posting ends, abbreviated @samp{e}. @end table @node Extending with Python, Ledger for Developers, Format Strings, Top @chapter Extending with Python Python can be used to extend your Ledger experience. But first, a word must be said about Ledger's data model, so that other things make sense later. @menu * Basic data traversal:: * Raw versus Cooked:: * Queries:: * Embedded Python:: * Amounts:: @end menu @node Basic data traversal, Raw versus Cooked, Extending with Python, Extending with Python @section Basic data traversal Every interaction with Ledger happens in the context of a Session. Even if you don't create a session manually, one is created for you by the top-level interface functions. The Session is where objects live like the Commodities that Amounts refer to. To make a Session useful, you must read a Journal into it, using the function `@code{read_journal}`. This reads Ledger data from the given file, populates a Journal object within the current Session, and returns a reference to that Journal object. Within the Journal live all the Transactions, Postings, and other objects related to your data. There are also AutomatedTransactions and PeriodicTransactions, etc. Here is how you would traverse all the postings in your data file: @smallexample import ledger for xact in ledger.read_journal("sample.dat").xacts(): for post in xact.posts(): print "Transferring %s to/from %s" % (post.amount, post.account) @end smallexample @node Raw versus Cooked, Queries, Basic data traversal, Extending with Python @section Raw versus Cooked Ledger data exists in one of two forms: raw and cooked. Raw objects are what you get from a traversal like the above, and represent exactly what was seen in the data file. Consider this journal: @smallexample @c input:validate = true (Assets:Cash) $100 2012-03-01 KFC Expenses:Food $100 Assets:Credit @end smallexample In this case, the @emph{raw} regular transaction in this file is: @smallexample @c input:validate 2012-03-01 KFC Expenses:Food $100 Assets:Credit @end smallexample While the @emph{cooked} form is: @smallexample @c input:validate 2012-03-01 KFC Expenses:Food $100 Assets:Credit $-100 (Assets:Cash) $100 @end smallexample So the easy way to think about raw vs. cooked is that raw is the unprocessed data, and cooked has had all considerations applied. When you traverse a Journal by iterating over its transactions, you are generally looking at raw data. In order to look at cooked data, you must generate a report of some kind by querying the journal: @smallexample for post in ledger.read_journal("sample.dat").query("food"): print "Transferring %s to/from %s" % (post.amount, post.account) @end smallexample The reason why queries iterate over postings instead of transactions is that queries often return only a ``slice'' of the transactions they apply to. You can always get at a matching posting's transaction by looking at its @code{xact} member: @smallexample last_xact = None for post in ledger.read_journal("sample.dat").query(""): if post.xact != last_xact: for post in post.xact.posts(): print "Transferring %s to/from %s" % (post.amount, post.account) last_xact = post.xact @end smallexample This query ends up reporting every cooked posting in the Journal, but does it transaction-wise. It relies on the fact that an unsorted report returns postings in the exact order they were parsed from the journal file. @node Queries, Embedded Python, Raw versus Cooked, Extending with Python @section Queries The Journal.query() method accepts every argument you can specify on the command-line, including @option{--options}. Since a query ``cooks'' the journal it applies to, only one query may be active for that journal at a given time. Once the query object is gone (after the for loop), then the data reverts back to its raw state. @node Embedded Python, Amounts, Queries, Extending with Python @section Embedded Python You can embed Python into your data files using the 'python' directive: @smallexample python import os def check_path(path_value): print "%s => %s" % (str(path_value), os.path.isfile(str(path_value))) return os.path.isfile(str(path_value)) tag PATH assert check_path(value) 2012-02-29 KFC ; PATH: somebogusfile.dat Expenses:Food $20 Assets:Cash @end smallexample Any Python functions you define this way become immediately available as valexpr functions. @node Amounts, , Embedded Python, Extending with Python @section Amounts When numbers come from Ledger, like post.amount, the type of the value is Amount. It can be used just like an ordinary number, except that addition and subtraction are restricted to amounts with the same commodity. If you need to create sums of multiple commodities, use a Balance. For example: @smallexample total = Balance() for post in ledger.read_journal("sample.dat").query(""): total += post.amount print total @end smallexample @node Ledger for Developers, Major Changes from version 2.6, Extending with Python, Top @chapter Ledger for Developers @menu * Internal Design:: * Journal File Format for Developers:: * Developer Commands:: * Ledger Development Environment:: @end menu @node Internal Design, Journal File Format for Developers, Ledger for Developers, Ledger for Developers @section Internal Design Ledger is developed as a tiered set of functionality, where lower tiers know nothing about the higher tiers. In fact, multiple libraries are built during the development the process, and link unit tests to these libraries, so that it is a link error for a lower tier to violate this modularity. Those tiers are: @itemize @item Utility code There's lots of general utility in Ledger for doing time parsing, using Boost.Regex, error handling, etc. It's all done in a way that can be reused in other projects as needed. @item Commoditized Amounts (amount_t, commodity_t and friends) A numerical abstraction combining multi-precision rational numbers (via GMP) with commodities. These structures can be manipulated like regular numbers in either C++ or Python (as Amount objects). @item Commodity Pool Commodities are all owned by a commodity pool, so that future parsing of amounts can link to the same commodity and established a consistent price history and record of formatting details. @item Balances Adds the concept of multiple amounts with varying commodities. Supports simple arithmetic, and multiplication and division with non-commoditized values. @item Price history Amounts have prices, and these are kept in a data graph which the amount code itself is only dimly aware of (there's three points of access so an amount can query its revalued price on a given date). @item Values Often the higher layers in Ledger don't care if something is an amount or a balance, they just want to add stuff to it or print it. For this, I created a type-erasure class, value_t/Value, into which many things can be stuffed and then operated on. They can contain amounts, balances, dates, strings, etc. If you try to apply an operation between two values that makes no sense (like dividing an amount by a balance), an error occurs at runtime, rather than at compile-time (as would happen if you actually tried to divide an @code{amount_t} by a @code{balance_t}). This is the core data type for the value expression language. @item Value expressions The next layer up adds functions and operators around the Value concept. This lets you apply transformations and tests to Values at runtime without having to bake it into C++. The set of functions available is defined by each object type in Ledger (posts, accounts, transactions, etc.), though the core engine knows nothing about these. At its base, it only knows how to apply operators to values, and how to pass them to and receive them from functions. @item Query expressions Expressions can be onerous to type at the command-line, so there's a shorthand for reporting called ``query expressions''. These add no functionality of their own, but are purely translated from the input string down to the corresponding value expression, for example the input string @samp{cash} is translated to @samp{(account =~ /cash/)}. This is a convenience layer. @item Format strings Format strings let you interpolate value expressions into strings, with the requirement that any interpolated value have a string representation. Really all this does is calculate the value expression in the current report context, call the resulting value's @code{to_string()} method, and stuffs the result into the output string. It also provides printf-like behavior, such as min/max width, right/left justification, etc. @item Journal items Next is a base type shared by anything that can appear in a journal: an item_t. It contains details common to all such parsed entities, like what file and line it was found on, etc. @item Journal posts The most numerous object found in a Journal, postings are a type of item that contain an account, an amount, a cost, and metadata. There are some other complications, like the account can be marked virtual, the amount could be an expression, etc. @item Journal transactions Postings are owned by transactions, always. This subclass of @code{item_t} knows about the date, the payee, etc. If a date or metadata tag is requested from a posting and it doesn't have that information, the transaction is queried to see if it can provide it. @item Journal accounts Postings are also shared by accounts, though the actual memory is managed by the transaction. Each account knows all the postings within it, but contains relatively little information of its own. @item The Journal object Finally, all transactions with their postings, and all accounts, are owned by a @code{journal_t} object. This is the go-to object for querying and reporting on your data. @item Textual journal parser There is a textual parser, wholly contained in @file{textual.cc}, which knows how to parse text into journal objects, which then get ``finalized'' and added to the journal. Finalization is the step that enforces the double-entry guarantee. @item Iterators Every journal object is ``iterable'', and these iterators are defined in @file{iterators.h} and @file{iterators.cc}. This iteration logic is kept out of the basic journal objects themselves for the sake of modularity. @item Comparators Another abstraction isolated to its own layer, this class encapsulating the comparison of journal objects, based on whatever value expression the user passed to @option{--sort @var{VEXPR}}. @item Temporaries Many reports bring pseudo-journal objects into existence, like postings which report totals in a @samp{Total} account. These objects are created and managed by a @code{temporaries_t} object, which gets used in many places by the reporting filters. @item Option handling There is an option handling subsystem used by many of the layers further down. It makes it relatively easy for me to add new options, and to have those option settings immediately accessible to value expressions. @item Session objects Every journal object is owned by a session, with the session providing support for that object. In GUI terms, this is the Controller object for the journal Data object, where every document window would be a separate session. They are all owned by the global scope. @item Report objects Every time you create any report output, a report object is created to determine what you want to see. In the Ledger REPL, a new report object is created every time a command is executed. In CLI mode, only one report object ever comes into being, as Ledger immediately exits after displaying the results. @item Reporting filters The way Ledger generates data is this: it asks the session for the current journal, and then creates an iterator applied to that journal. The kind of iterator depends on the type of report. This iterator is then walked, and every object yielded from the iterator is passed to an ``item handler'', whose type is directly related to the type of the iterator. There are many, many item handlers, which can be chained together. Each one receives an item (post, account, xact, etc.), performs some action on it, and then passes it down to the next handler in the chain. There are filters which compute the running totals; that queue and sort all the input items before playing them back out in a new order; that filter out items which fail to match a predicate, etc. Almost every reporting feature in Ledger is related to one or more filters. Looking at @file{filters.h}, there are over 25 of them defined currently. @item The filter chain How filters get wired up, and in what order, is a complex process based on all the various options specified by the user. This is the job of the chain logic, found entirely in @file{chain.cc}. It took a really long time to get this logic exactly right, which is why I haven't exposed this layer to the Python bridge yet. @item Output modules Although filters are great and all, in the end you want to see stuff. This is the job of special ``leaf'' filters called output modules. They are implemented just like a regular filter, but they don't have a ``next'' filter to pass the data on down to. Instead, they are the end of the line and must do something with the item that results in the user seeing something on their screen or in a file. @item Select queries Select queries know a lot about everything, even though they implement their logic by implementing the user's query in terms of all the other features thus presented. Select queries have no functionality of their own, they are simple a shorthand to provide access to much of Ledger's functionality via a cleaner, more consistent syntax. @item The Global Scope There is a master object which owns every other objects, and this is Ledger's global scope. It creates the other objects, provides REPL behavior for the command-line utility, etc. In GUI terms, this is the Application object. @item The Main Driver This creates the global scope object, performs error reporting, and handles command-line options which must precede even the creation of the global scope, such as @option{--debug @var{CODE}}. @end itemize And that's Ledger in a nutshell. All the rest are details, such as which value expressions each journal item exposes, how many filters currently exist, which options the report and session scopes define, etc. @node Journal File Format for Developers, Developer Commands, Internal Design, Ledger for Developers @section Journal File Format for Developers This chapter offers a complete description of the journal data format, suitable for implementers in other languages to follow. For users, the chapter on keeping a journal is less extensive, but more typical of common usage (@pxref{Keeping a Journal}). Data is collected in the form of @dfn{transactions} which occur in one or more @dfn{journal files}. Each transaction, in turn, is made up of one or more @dfn{postings}, which describe how @dfn{amounts} flow from one @dfn{account} to another. Here is an example of the simplest of journal files: @smallexample @c input:validate 2010/05/31 Just an example Expenses:Some:Account $100.00 Income:Another:Account @end smallexample In this example, there is a transaction date, a payee, or description of the transaction, and two postings. The postings show movement of one hundred dollars from an account within the Income hierarchy, to the specified expense account. The name and meaning of these accounts is arbitrary, with no preferences implied, although you will find it useful to follow standard accounting practices (@pxref{Principles of Accounting with Ledger}). Since an amount is missing from the second posting, it is assumed to be the inverse of the first. This guarantees the cardinal rule of double-entry accounting: the sum of every transaction must balance to zero, or it is in error. Whenever Ledger encounters a @dfn{null posting} in a transaction, it uses it to balance the remainder. It is also typical, though not enforced, to think of the first posting as the destination, and the final as the source. Thus, the amount of the first posting is typically positive. Consider: @smallexample @c input:validate 2010/05/31 An income transaction Assets:Checking $1,000.00 Income:Salary 2010/05/31 An expense transaction Expenses:Dining $100.00 Assets:Checking @end smallexample @menu * Comments and meta-data:: * Specifying Amounts:: * Posting costs:: * Primary commodities:: @end menu @node Comments and meta-data, Specifying Amounts, Journal File Format for Developers, Journal File Format for Developers @subsection Comments and meta-data Comments are generally started using a @samp{;}. However, in order to increase compatibility with other text manipulation programs and methods three additional comment characters are valid if used at the beginning of a line: @samp{#}, @samp{|}, and @samp{*}. @node Specifying Amounts, Posting costs, Comments and meta-data, Journal File Format for Developers @subsection Specifying Amounts @cindex amounts The heart of a journal is the amounts it records, and this fact is reflected in the diversity of amount expressions allowed. All of them are covered here, though it must be said that sometimes, there are multiple ways to achieve a desired result. @emph{Note:} It is important to note that there must be at least two spaces between the end of the account and the beginning of the amount (including a commodity designator). @menu * Integer Amounts:: * Commoditized Amounts:: @end menu @node Integer Amounts, Commoditized Amounts, Specifying Amounts, Specifying Amounts @subsubsection Integer Amounts In the simplest form, bare decimal numbers are accepted: @smallexample @c input:validate 2010/05/31 An income transaction Assets:Checking 1000.00 Income:Salary @end smallexample @cindex uncommoditized amounts Such amounts may only use an optional period for a decimal point. These are referred to as @dfn{integer amounts} or @dfn{uncommoditized amounts}. In most ways they are similar to @dfn{commoditized amounts}, but for one significant difference: They always display in reports with @dfn{full precision}. More on this in a moment. For now, a word must be said about how Ledger stores numbers. Every number parsed by Ledger is stored internally as an infinite-precision rational value. Floating-point math is never used, as it cannot be trusted to maintain precision of values. So, in the case of @samp{1000.00} above, the internal value is @samp{100000/100}. While rational numbers are great at not losing precision, the question arises: How should they be displayed? A number like @samp{100000/100} is no problem, since it represents a clean decimal fraction. But what about when the number @samp{1/1} is divided by three? How should one print @samp{1/3}, an infinitely repeating decimal? Ledger gets around this problem by rendering rationals into decimal at the last possible moment, and only for display. As such, some rounding must, at times, occur. If this rounding would affect the calculation of a running total, special accommodation postings are generated to make you aware it has happened. In practice, it happens rarely, but even then it does not reflect adjustment of the @emph{internal amount}, only the displayed amount. What has still not been answered is how Ledger rounds values. Should @samp{1/3} be printed as @samp{0.33} or @samp{0.33333}? For commoditized amounts, the number of decimal places is decided by observing how each commodity is used; but in the case of integer amounts, an arbitrary factor must be chosen. Initially, this factor is six. Thus, @samp{1/3} is printed back as @samp{0.333333}. Further, this rounding factor becomes associated with each particular value, and is carried through mathematical operations. For example, if that particular number were multiplied by itself, the decimal precision of the result would be twelve. Addition and subtraction do not affect precision. Since each integer amount retains its own display precision, this is called @dfn{full precision}, as opposed to commoditized amounts, which always look to their commodity to know what precision they should round to, and so use @dfn{commodity precision}. @node Commoditized Amounts, , Integer Amounts, Specifying Amounts @subsubsection Commoditized Amounts A @dfn{commoditized amount} is an integer amount which has an associated commodity. This commodity can appear before or after the amount, and may or may not be separated from it by a space. Most characters are allowed in a commodity name, except for the following: @itemize @bullet @item Any kind of white-space @item Numerical digits @item Punctuation: @code{.,;:?!} @item Mathematical and logical operators: @code{-+*/^&|=} @item Bracketing characters: @code{<>[]()}@{@} @item The at symbol: @code{@@} @end itemize And yet, any of these may appear in a commodity name if it is surrounded by double quotes, for example: @smallexample 100 "EUN+133" @end smallexample If a @dfn{quoted commodity} is found, it is displayed in quotes as well, to avoid any confusion as to which part is the amount, and which part is the commodity. Another feature of commoditized amounts is that they are reported back in the same form as parsed. If you specify dollar amounts using @samp{$100}, they will print the same; likewise with @samp{100 $} or @samp{$100.000}. You may even use decimal commas, such as @samp{$100,00}, or thousand-marks, as in @samp{$10,000.00}. These display characteristics become associated with the commodity, with the result being that all amounts of the same commodity are reported consistently. Where this is most noticeable is the @dfn{display precision}, which is determined by the most precise value seen for a given commodity---in most cases. Ledger makes a distinction between @dfn{observed amounts} and unobserved amounts. An observed amount is critiqued by Ledger to determine how amounts using that commodity should be displayed; unobserved amounts are significant in their value only---no matter how they are specified, it does not change how other amounts in that commodity will be displayed. An example of this is found in cost expressions, covered next. @node Posting costs, Primary commodities, Specifying Amounts, Journal File Format for Developers @subsection Posting costs You have seen how to specify either a commoditized or an integer amount for a posting. But what if the amount you paid for something was in one commodity, and the amount received was another? There are two main ways to express this: @smallexample @c input:validate 2010/05/31 Farmer's Market Assets:My Larder 100 apples Assets:Checking -$20.00 @end smallexample In this example, you have paid twenty dollars for one hundred apples. The cost to you is twenty cents per apple, and Ledger calculates this implied cost for you. You can also make the cost explicit using a @dfn{cost amount}: @smallexample @c input:validate 2010/05/31 Farmer's Market Assets:My Larder 100 apples @@ $0.200000 Assets:Checking @end smallexample Here the @dfn{per-unit cost} is given explicitly in the form of a cost amount; and since cost amounts are @emph{unobserved}, the use of six decimal places has no effect on how dollar amounts are displayed in the final report. You can also specify the @dfn{total cost}: @smallexample @c input:validate 2010/05/31 Farmer's Market Assets:My Larder 100 apples @@@@ $20 Assets:Checking @end smallexample These three forms have identical meaning. In most cases the first is preferred, but the second two are necessary when more than two postings are involved: @smallexample @c input:validate 2010/05/31 Farmer's Market Assets:My Larder 100 apples @@ $0.200000 Assets:My Larder 100 pineapples @@ $0.33 Assets:My Larder 100 "crab apples" @@ $0.04 Assets:Checking @end smallexample Here the implied cost is @samp{$57.00}, which is entered into the null posting automatically so that the transaction balances. @node Primary commodities, , Posting costs, Journal File Format for Developers @subsection Primary commodities @findex --market @findex --basis In every transaction involving more than one commodity, there is always one which is the @dfn{primary commodity}. This commodity should be thought of as the exchange commodity, or the commodity used to buy and sell units of the other commodity. In the fruit examples above, dollars are the primary commodity. This is decided by Ledger based on the placement of the commodity in the transaction: @smallexample @c input:validate 2010/05/31 Sample Transaction Expenses 100 secondary Assets -50 primary 2010/05/31 Sample Transaction Expenses 100 secondary @@ 0.5 primary Assets 2010/05/31 Sample Transaction Expenses 100 secondary @@@@ 50 primary Assets @end smallexample The only case where knowledge of primary versus secondary comes into play is in reports that use the @option{--market (-V)} or @option{--basis (-B)} options. With these, only primary commodities are shown. If a transaction uses only one commodity, this commodity is also considered a primary. In fact, when Ledger goes about ensuring that all transactions balance to zero, it only ever asks this of primary commodities. @node Developer Commands, Ledger Development Environment, Journal File Format for Developers, Ledger for Developers @section Developer Commands @menu * @command{echo}:: * @command{reload}:: * @command{source}:: * Debug Options:: * Pre-Commands:: @end menu @node @command{echo}, @command{reload}, Developer Commands, Developer Commands @subsection @command{echo} @findex echo This command simply echoes its argument back to the output. @node @command{reload}, @command{source}, @command{echo}, Developer Commands @subsection @command{reload} @findex reload Forces ledger to reload any journal files. This function exists to support external programs controlling a running ledger process and does nothing for a command-line user. @node @command{source}, Debug Options, @command{reload}, Developer Commands @subsection @command{source} @findex source The @command{source} command takes a journal file as an argument and parses it checking for errors; no other reports are generated, and no other arguments are necessary. Ledger will return success if no errors are found. @node Debug Options, Pre-Commands, @command{source}, Developer Commands @subsection Debug Options These options are primarily for Ledger developers, but may be of some use to a user trying something new. @ftable @option @item --args-only Ignore init files and environment variables for the ledger run. @item --debug @var{CODE} If Ledger has been built with debug options this will provide extra data during the run. Listed below are the available @var{CODES} to debug. You can provide multiple using a regex expression like "@code{(account.display|expr.calc)}". @multitable @columnfractions .32 .43 .27 @item @code{account.display} @tab @code{draft.xact} @tab @code{option.names} @item @code{account.sorted} @tab @code{expr.calc} @tab @code{org.next_amount} @item @code{amount.commodities} @tab @code{expr.compile} @tab @code{org.next_total} @item @code{amount.convert} @tab @code{expr.merged.compile} @tab @code{parser.error} @item @code{amount.is_zero} @tab @code{filters.changed_value} @tab @code{pool.commodities} @item @code{amount.parse} @tab @code{filters.changed_value.rounding} @tab @code{post.assign} @item @code{amount.price} @tab @code{filters.collapse} @tab @code{python.init} @item @code{amount.refs} @tab @code{filters.forecast} @tab @code{python.interp} @item @code{amount.roundto} @tab @code{filters.interval} @tab @code{query.mask} @item @code{amount.truncate} @tab @code{filters.revalued} @tab @code{report.predicate} @item @code{amount.unround} @tab @code{format.abbrev} @tab @code{scope.search} @item @code{annotate.less} @tab @code{format.expr} @tab @code{scope.symbols} @item @code{archive.journal} @tab @code{generate.post} @tab @code{select.parse} @item @code{auto.columns} @tab @code{generate.post.string} @tab @code{textual.include} @item @code{budget.generate} @tab @code{history.find} @tab @code{textual.parse} @item @code{commodity.annotated.strip} @tab @code{history.map} @tab @code{timelog} @item @code{commodity.annotations} @tab @code{item.meta} @tab @code{times.epoch} @item @code{commodity.compare} @tab @code{ledger.read} @tab @code{times.interval} @item @code{commodity.download} @tab @code{ledger.validate} @tab @code{times.parse} @item @code{commodity.exchange} @tab @code{lookup} @tab @code{value.sort} @item @code{commodity.price.find} @tab @code{lookup.account} @tab @code{value.storage.refcount} @item @code{commodity.prices.add} @tab @code{mask.match} @tab @code{xact.extend} @item @code{commodity.prices.find} @tab @code{memory.debug} @tab @code{xact.extend.cleared} @item @code{csv.mappings} @tab @code{op.memory} @tab @code{xact.extend.fail} @item @code{csv.parse} @tab @code{option.args} @tab @code{xact.finalize} @end multitable @ @item --trace @var{INT} Enable tracing. The @var{INT} specifies the level of trace desired: @multitable @columnfractions .3 .7 @item @code{LOG_OFF} @tab 0 @item @code{LOG_CRIT} @tab 1 @item @code{LOG_FATAL} @tab 2 @item @code{LOG_ASSERT} @tab 3 @item @code{LOG_ERROR} @tab 4 @item @code{LOG_VERIFY} @tab 5 @item @code{LOG_WARN} @tab 6 @item @code{LOG_INFO} @tab 7 @item @code{LOG_EXCEPT} @tab 8 @item @code{LOG_DEBUG} @tab 9 @item @code{LOG_TRACE} @tab 10 @item @code{LOG_ALL} @tab 11 @end multitable @ @item --verbose @itemx -v Print detailed information on the execution of Ledger. @item --verify Enable additional assertions during run-time. This causes a significant slowdown. When combined with @option{--debug @var{CODE}} ledger will produce memory trace information. @item --verify-memory Verify that every constructed object is properly destructed. This is for debugging purposes only. @item --version Print version information and exit. @end ftable @node Pre-Commands, , Debug Options, Developer Commands @subsection Pre-Commands @cindex pre-commands Pre-commands are useful when you aren't sure how a command or option will work. The difference between a pre-command and a regular command is that pre-commands ignore the journal data file completely, nor is the user's init file read. @ftable @command @item eval @var{VEXPR} Evaluate the given value expression against the model transaction. @item format @var{FORMAT_STRING} Print details of how ledger uses the given formatting description and apply it against a model transaction. @item generate Randomly generates syntactically valid Ledger data from a seed. Used by the @samp{GenerateTests} harness for development testing. @item parse @var{VEXPR} @itemx expr @var{VEXPR} Print details of how ledger uses the given value expression description and apply it against a model transaction. @item period @var{PERIOD_EXPRESSION} Evaluate the given period and report how Ledger interprets it: @smallexample @c command:51F6A2C $ ledger period "this year" --now 2011-01-01 @end smallexample @smallexample @c output:51F6A2C --- Period expression tokens --- TOK_THIS: this TOK_YEAR: year END_REACHED: <EOF> --- Before stabilization --- range: in year 2011 --- After stabilization --- range: in year 2011 start: 11-Jan-01 finish: 12-Jan-01 --- Sample dates in range (max. 20) --- 1: 11-Jan-01 @end smallexample @item query @itemx args Evaluate the given arguments and report how Ledger interprets it against the following model transaction: @smallexample @c command:validate $ ledger query "/Book/" @end smallexample @smallexample --- Input arguments --- ("/Book/") --- Context is first posting of the following transaction --- 2004/05/27 Book Store ; This note applies to all postings. :SecondTag: Expenses:Books 20 BOOK @@ $10 ; Metadata: Some Value ; Typed:: $100 + $200 ; :ExampleTag: ; Here follows a note describing the posting. Liabilities:MasterCard $-200.00 --- Input expression --- (account =~ /Book/) --- Text as parsed --- (account =~ /Book/) --- Expression tree --- 0x7fd639c0da40 O_MATCH (1) 0x7fd639c10170 IDENT: account (1) 0x7fd639c10780 VALUE: /Book/ (1) --- Compiled tree --- 0x7fd639c10520 O_MATCH (1) 0x7fd639c0d6c0 IDENT: account (1) 0x7fd639c0d680 FUNCTION (1) 0x7fd639c10780 VALUE: /Book/ (1) --- Calculated value --- true @end smallexample @item script @value{FIXME:UNDOCUMENTED} @item template Shows the insertion template that the @command{xact} sub-command generates. This is a debugging command. @end ftable @node Ledger Development Environment, , Developer Commands, Ledger for Developers @section Ledger Development Environment @menu * @file{acprep} build configuration tool:: * Testing Framework:: @end menu @node @file{acprep} build configuration tool, Testing Framework, Ledger Development Environment, Ledger Development Environment @subsection @file{acprep} build configuration tool @node Testing Framework, , @file{acprep} build configuration tool, Ledger Development Environment @subsection Testing Framework Ledger source ships with a fairly complete set of tests to verify that all is well, and no old errors have resurfaced. Tests are run individually with @file{ctest}. All tests can be run using @code{make check} or @code{ninja check} depending on which build tool you prefer. Once built, the ledger executable resides under the @file{build} subdirectory in the source tree. Tests are built and stored in the @file{test} subdirectory for the build. For example, @file{~/ledger/build/ledger/opt/test}. @menu * Running Tests:: * Writing Tests:: @end menu @node Running Tests, Writing Tests, Testing Framework, Testing Framework @subsubsection Running Tests The complete test suite can be run from the build directory using the check option for the build tool you use. For example, @code{make check}. The entire test suit lasts around a minute for the optimized built and many times longer for the debug version. While developing and debugging, running individual tests can save a great deal of time. Individual tests can be run from the @file{test} subdirectory of the build location. To execute a single test use @code{ctest -V -R regex}, where the regex matches the name of the test you want to build. There are nearly 300 tests stored under the @file{test} subdirectory in the main source distribution. They are broken into two broad categories, baseline and regression. To run the @file{5FBF2ED8} test, for example, issue @code{ctest -V -R "5FB"}. @node Writing Tests, , Running Tests, Testing Framework @subsubsection Writing Tests To write a new test first decide to which broad category the test belongs: baseline or regression. Depending on the category tests are named differently baseline tests are prefixed with their type, e.g. @samp{cmd} (@pxref{Baseline Test Types} for valid types), whereas regressions are either named after the bug id, e.g. @samp{1234.test} or uuid @samp{91416D62.test}. In case several test files belong to the same bug number the files by appending @code{_X} where @samp{X} is the number of the test, e.g. @samp{1234_1.test}, @samp{1234_2.test}. Baseline Test Types: @anchor{Baseline Test Types} @table @code @item cmd Ledger commands like @command{register} or @command{balance} @item dir Ledger directives like @code{account} or @code{alias} @item feat Ledger features such as balance assertions in journal file @item opt Ledger options such as @option{--period} or @option{--format} @end table A ledger test file contains three sections: @enumerate @item the journal data used for the test, this can be empty in certain scenarios @item the ledger command-line options used for the test @item the expected output @end enumerate Ledger has a special command directive for tests, everything between @code{test} and @code{end test} is treated like a comment, so every Ledger test is automatically a valid Ledger file. The test scripts take the remainder of the @code{test} line and use it as command-line arguments for ledger, the text enclosed in @code{test} and @code{end test} is expected output, for example: @smallexample @c input:validate ; This is the journal data year 2014 12/24 (C0d3) Santa Claus Assets:Bank ¤ -150,00 Expenses:Presents ; The following line specifies the ledger command-line options for this test and ; everything between the next line and `end test` specifies the expected output test reg --payee=code 14-Dec-24 C0d3 Assets:Bank ¤ -150,00 ¤ -150,00 14-Dec-24 C0d3 Expenses:Presents ¤ 150,00 0 end test @end smallexample When it is necessary to test for errors printed to @code{stderr} redirect the test output by adding @code{->} to the @code{test} line and match the expected error text in an @code{__ERROR__} section: @smallexample 2014/01/01 * Acme Corporation Assets:Bank:Checking ¤ 1.000,00 [Fund:Vacation] ¤ 300,00 [Fund:Studies] ¤ 600,00 Income:Salary ¤ -2.000,00 test reg -> __ERROR__ While parsing file "$FILE", line 5: While balancing transaction from "$FILE", lines 1-5: > 2014/01/01 * Acme Corporation > Assets:Bank:Checking ¤ 1.000,00 > [Fund:Vacation] ¤ 300,00 > [Fund:Studies] ¤ 600,00 > Income:Salary ¤ -2.000,00 Unbalanced remainder is: ¤ -100,00 Amount to balance against: ¤ 1.900,00 Error: Transaction does not balance end test @end smallexample A special @code{$FILE} variable can be used to match the journal filename used during the test. To add new tests to the test suite use the rebuild_cache option for the build tool you use, for example @code{make rebuild_cache}, now the new tests can be run as documented in @ref{Running Tests}. @node Major Changes from version 2.6, Example Journal File, Ledger for Developers, Top @chapter Major Changes from version 2.6 The following have been removed from Ledger 3.0: @itemize @item OFX support. @item GnuCash file import. @item The option @option{--performance (-g)}. @item The balance report now defaults to showing all relevant accounts. This is the opposite of 2.x. That is, @command{bal} in 3.0 does what @samp{-s bal} did in 2.x. To see 2.6 behavior, use @option{--collapse (-n)} option in 3.0, like @samp{bal -n}. The @option{--subtotal (-s)} option no longer has any effect on balance reports. @end itemize @noindent The following are deprecated in Ledger 3.0: @itemize @item Single character value expressions are deprecated and should be changed to the new value expressions available in 3.0 @item The following environment variables have been renamed in Ledger 3.0: @table @env @item LEDGER is now @env{LEDGER_FILE}, @item LEDGER_INIT is now @env{LEDGER_INIT_FILE}, @item PRICE_HIST is now @env{LEDGER_PRICE_DB}, @item PRICE_EXP is now @env{LEDGER_PRICE_EXP}. @end table @end itemize @node Example Journal File, Miscellaneous Notes, Major Changes from version 2.6, Top @appendix Example Journal File The following journal file is included with the source distribution of ledger. It is called @file{drewr3.dat} and exhibits many ledger features, include automatic and virtual transactions, @smallexample @c input:validate ; -*- ledger -*- = /^Income/ (Liabilities:Tithe) 0.12 ;~ Monthly ; Assets:Checking $500.00 ; Income:Salary ;~ Monthly ; Expenses:Food $100 ; Assets 2010/12/01 * Checking balance Assets:Checking $1,000.00 Equity:Opening Balances 2010/12/20 * Organic Co-op Expenses:Food:Groceries $ 37.50 ; [=2011/01/01] Expenses:Food:Groceries $ 37.50 ; [=2011/02/01] Expenses:Food:Groceries $ 37.50 ; [=2011/03/01] Expenses:Food:Groceries $ 37.50 ; [=2011/04/01] Expenses:Food:Groceries $ 37.50 ; [=2011/05/01] Expenses:Food:Groceries $ 37.50 ; [=2011/06/01] Assets:Checking $ -225.00 2010/12/28=2011/01/01 Acme Mortgage Liabilities:Mortgage:Principal $ 200.00 Expenses:Interest:Mortgage $ 500.00 Expenses:Escrow $ 300.00 Assets:Checking $ -1000.00 2011/01/02 Grocery Store Expenses:Food:Groceries $ 65.00 Assets:Checking 2011/01/05 Employer Assets:Checking $ 2000.00 Income:Salary 2011/01/14 Bank ; Regular monthly savings transfer Assets:Savings $ 300.00 Assets:Checking 2011/01/19 Grocery Store Expenses:Food:Groceries $ 44.00 ; hastag: not block Assets:Checking 2011/01/25 Bank ; Transfer to cover car purchase Assets:Checking $ 5,500.00 Assets:Savings ; :nobudget: apply tag hastag: true apply tag nestedtag: true 2011/01/25 Tom's Used Cars Expenses:Auto $ 5,500.00 ; :nobudget: Assets:Checking 2011/01/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard end tag 2011/12/01 Sale Assets:Checking:Business $ 30.00 Income:Sales end tag @end smallexample @node Miscellaneous Notes, Concepts Index, Example Journal File, Top @appendix Miscellaneous Notes Various notes from the discussion list that I haven't incorporated in to the main body of the documentation. @menu * Cookbook:: @end menu @node Cookbook, , Miscellaneous Notes, Miscellaneous Notes @section Cookbook @menu * Invoking Ledger:: * Ledger Files:: @end menu @node Invoking Ledger, Ledger Files, Cookbook, Cookbook @subsection Invoking Ledger @smallexample @c command:validate $ ledger --group-by "tag('trip')" bal @end smallexample @c FIXME: The following example fails to validate due to: @c While applying is_realzero to : @c Error: Cannot determine if an uninitialized value is really zero @c @smallexample @c command:validate @c $ ledger reg --sort "tag('foo')" %foo @c @end smallexample @smallexample @c command:validate $ ledger cleared VWCU NFCU Tithe Misentry @end smallexample @smallexample @c command:validate $ ledger register Joint --uncleared @end smallexample @smallexample @c command:validate $ ledger register Checking --sort d -d 'd>[2011/04/01]' until 2011/05/25 @end smallexample @node Ledger Files, , Invoking Ledger, Cookbook @subsection Ledger Files @smallexample @c input:validate = /^Income:Taxable/ (Liabilities:Tithe Owed) -0.1 = /Noah/ (Liabilities:Tithe Owed) -0.1 = /Jonah/ (Liabilities:Tithe Owed) -0.1 = /Tithe/ (Liabilities:Tithe Owed) -1.0 @end smallexample @node Concepts Index, Commands & Options Index, Miscellaneous Notes, Top @unnumbered Concepts Index @printindex cp @node Commands & Options Index, , Concepts Index, Top @unnumbered Commands & Options Index @printindex fn @bye �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/doc/version.texi.in��������������������������������������������������������������������0000664�0000000�0000000�00000000604�14411236400�0016506�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@set Ledger_VERSION_MAJOR @Ledger_VERSION_MAJOR@ @set Ledger_VERSION_MINOR @Ledger_VERSION_MINOR@ @set Ledger_VERSION_PATCH @Ledger_VERSION_PATCH@ @set Ledger_VERSION_PRERELEASE @Ledger_VERSION_PRERELEASE@ @set Ledger_VERSION_DATE @Ledger_VERSION_DATE@ @set VERSION @value{Ledger_VERSION_MAJOR}.@value{Ledger_VERSION_MINOR}.@value{Ledger_VERSION_PATCH}@value{Ledger_VERSION_PRERELEASE} ����������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/flake.lock�����������������������������������������������������������������������������0000664�0000000�0000000�00000000774�14411236400�0014720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "nodes": { "nixpkgs": { "locked": { "lastModified": 1675153841, "narHash": "sha256-EWvU3DLq+4dbJiukfhS7r6sWZyJikgXn6kNl7eHljW8=", "owner": "NixOS", "repo": "nixpkgs", "rev": "ea692c2ad1afd6384e171eabef4f0887d2b882d3", "type": "github" }, "original": { "id": "nixpkgs", "type": "indirect" } }, "root": { "inputs": { "nixpkgs": "nixpkgs" } } }, "root": "root", "version": 7 } ����ledger-3.3.2/flake.nix������������������������������������������������������������������������������0000664�0000000�0000000�00000005463�14411236400�0014566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ description = "A double-entry accounting system with a command-line reporting interface"; nixConfig.bash-prompt = "ledger$ "; outputs = { self, nixpkgs }: let usePython = true; gpgmeSupport = true; forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; in { packages = forAllSystems (system: let pkgs = nixpkgsFor.${system}; in with pkgs; { ledger = stdenv.mkDerivation { pname = "ledger"; version = "3.3.2-${self.shortRev or "dirty"}"; src = self; outputs = [ "out" "dev" ] ++ lib.optionals usePython [ "py" ]; buildInputs = [ gmp mpfr libedit gnused ] ++ lib.optionals gpgmeSupport [ gpgme ] ++ (if usePython then [ python3 (boost.override { enablePython = true; python = python3; }) ] else [ boost ]); nativeBuildInputs = [ cmake texinfo tzdata ]; enableParallelBuilding = true; cmakeFlags = [ "-DCMAKE_INSTALL_LIBDIR=lib" "-DBUILD_DOCS:BOOL=ON" "-DUSE_PYTHON:BOOL=${if usePython then "ON" else "OFF"}" "-DUSE_GPGME:BOOL=${if gpgmeSupport then "ON" else "OFF"}" ]; # by default, it will query the python interpreter for its sitepackages location # however, that would write to a different nixstore path, pass our own sitePackages location prePatch = lib.optionalString usePython '' substituteInPlace src/CMakeLists.txt \ --replace 'DESTINATION ''${Python_SITEARCH}' 'DESTINATION "${placeholder "py"}/${python3.sitePackages}"' ''; installTargets = [ "doc" "install" ]; checkPhase = '' export LD_LIBRARY_PATH=$PWD export DYLD_LIBRARY_PATH=$PWD ctest -j$NIX_BUILD_CORES ''; doCheck = true; meta = with lib; { description = "A double-entry accounting system with a command-line reporting interface"; homepage = "https://ledger-cli.org/"; changelog = "https://github.com/ledger/ledger/raw/v${version}/NEWS.md"; license = lib.licenses.bsd3; longDescription = '' Ledger is a powerful, double-entry accounting system that is accessed from the UNIX command-line. This may put off some users, as there is no flashy UI, but for those who want unparalleled reporting access to their data, there really is no alternative. ''; platforms = lib.platforms.all; maintainers = with maintainers; [ jwiegley ]; }; }; }); defaultPackage = forAllSystems (system: self.packages.${system}.ledger); }; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/�����������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0013522�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/Makefile���������������������������������������������������������������������������0000664�0000000�0000000�00000003200�14411236400�0015155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Uncomment these if you are on OS X and want to build universal libraries. # This is only important if you intend to produce a Ledger binary for # installation. STOW_ROOT = /usr/local/Cellar/boost PRODUCTS = $(HOME)/Products GCC_VERSION = 4.7 BOOST_VERSION = 1_52_0 CC = gcc-mp-$(GCC_VERSION) ifeq ($(CC),clang) CXX = clang++ LD = llvm-ld DIR_SUFFIX = clang OPTJ = else CXX = g++-mp-$(GCC_VERSION) LD = gcc-mp-$(GCC_VERSION) DIR_SUFFIX = gcc$(subst .,,$(GCC_VERSION)) OPTJ = #-j8 endif CFLAGS = $(CPPFLAGS) -g2 -ggdb LDFLAGS = -g2 -ggdb BOOST_SOURCE = boost-release ifeq ($(GCC_VERSION),4.7) BOOST_DEFINES = define=_GLIBCXX__PTHREADS=1 else BOOST_DEFINES = endif ifeq ($(CC),clang) BOOST_TOOLSET = clang else BOOST_TOOLSET = darwin endif BOOST_FLAGS = toolset=$(BOOST_TOOLSET) --layout=versioned \ link=shared threading=single $(BOOST_DEFINES) BOOST_DIR = boost_$(BOOST_VERSION)-$(DIR_SUFFIX) BOOST_STOW = $(STOW_ROOT)/$(BOOST_VERSION) BOOST_BUILD = $(PRODUCTS)/$(BOOST_DIR) all: boost-build prepare-boost: perl -i -pe 's/local command = \[ common\.get-invocation-command darwin : g\+\+ : .*/local command = [ common.get-invocation-command darwin : g++ : $(CXX) ] ;/;' $(BOOST_SOURCE)/tools/build/v2/tools/darwin.jam perl -i -pe 's/flags darwin\.compile OPTIONS : -no-cpp-precomp -gdwarf-2 (-fexceptions )?;/flags darwin\.compile OPTIONS : -gdwarf-2 \1;/;' $(BOOST_SOURCE)/tools/build/v2/tools/darwin.jam boost-build: prepare-boost (cd $(BOOST_SOURCE) && \ sh bootstrap.sh && \ ./b2 $(OPTJ) debug release --prefix=$(BOOST_STOW) \ --build-dir=$(BOOST_BUILD) $(BOOST_FLAGS) install) clean: -rm -fr $(BOOST_STOW) $(BOOST_BUILD) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/build-gcc.sh�����������������������������������������������������������������������0000775�0000000�0000000�00000001234�14411236400�0015712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # This build script is for OS X Lion users who have compiled openmpi and # clang-3.1 from MacPorts. I build my own Boost instead of using MacPorts' # Boost in order to get better debugging support, and to link with libc++. export PATH=$PATH:/opt/local/lib/openmpi/bin cat > ~/user-config.jam <<EOF using clang-darwin : : "/usr/local/bin/clang++" : <cxxflags>-std=c++11 <include>/usr/local/include ; EOF # jww (2012-04-24): This is still linking against /usr/lib/libc++.1.dylib # instead of /usr/local/lib/libc++.1.dylib make "$@" OPTJ=-j20 \ BOOST_DEFINES="-sICU_PATH=/opt/local cxxflags=\"-I/opt/local/include\" linkflags=\"-L/opt/local/lib\"" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/build-icc.sh�����������������������������������������������������������������������0000775�0000000�0000000�00000001426�14411236400�0015717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # This build script is for OS X Lion users who have compiled openmpi and # clang-3.1 from MacPorts. I build my own Boost instead of using MacPorts' # Boost in order to get better debugging support, and to link with libc++. export PATH=$PATH:/opt/local/lib/openmpi/bin cat > ~/user-config.jam <<EOF using intel : : "/opt/intel/bin/icc" : ; EOF # jww (2012-04-24): This is still linking against /usr/lib/libc++.1.dylib # instead of /usr/local/lib/libc++.1.dylib make CXX=icc LD=icc CC=icc AR=xiar OPTJ=-j20 \ BOOST_TOOLSET=intel DIR_SUFFIX=intel \ BOOST_DEFINES="-sINTEL_PATH=/opt/intel/bin -sINTEL_VERSION=12 -sICU_PATH=/usr/local cxxflags=\"-I/usr/local/include -I/opt/local/include\" linkflags=\"-L/usr/local/lib -L/opt/local/lib\"" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/build.sh���������������������������������������������������������������������������0000775�0000000�0000000�00000001731�14411236400�0015162�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # This build script is for OS X Lion users who have compiled openmpi and # clang-3.1 from MacPorts. I build my own Boost instead of using MacPorts' # Boost in order to get better debugging support, and to link with libc++. #export PATH=$PATH:/opt/local/lib/openmpi/bin cat > ~/user-config.jam <<EOF using clang-darwin : : "/usr/local/bin/clang++" : <cxxflags>-std=c++11 ; EOF # jww (2012-04-24): This is still linking against /usr/lib/libc++.1.dylib # instead of /usr/local/lib/libc++.1.dylib make CXX=clang++ LD=clang++ CC=clang OPTJ=-j20 \ BOOST_TOOLSET=clang-darwin DIR_SUFFIX=clang31 \ BOOST_DEFINES="-sHAVE_ICONV=1 -sHAVE_ICU=1 -sICU_PATH=/usr/local/opt/icu4c cxxflags=\"-g -std=c++11 $* -nostdlibinc -isystem /usr/local/include -isystem /usr/local/include/c++/v1 -isystem /usr/include -stdlib=libc++\" linkflags=\"-g $* -L/usr/local/lib -L/usr/lib /usr/local/lib/libc++.dylib -stdlib=libc++\"" ���������������������������������������ledger-3.3.2/lib/utfcpp/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0015023�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0015353�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/.circleci/���������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0017206�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/.circleci/config.yml�����������������������������������������������������0000664�0000000�0000000�00000000455�14411236400�0021202�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������version: 2 jobs: build: docker: - image: nemtrif/utf8cpp:3.1.3 steps: - checkout - run: git submodule update --init --recursive --remote - run: mkdir build - run: cd build && cmake .. - run: cd build && cmake --build . - run: cd build && ctest -VV �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/.gitignore���������������������������������������������������������������0000664�0000000�0000000�00000000061�14411236400�0017340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VS Code: .vscode/ # Often used by CMake build/�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/.gitmodules��������������������������������������������������������������0000664�0000000�0000000�00000000130�14411236400�0017522�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[submodule "extern/ftest"] path = extern/ftest url = https://github.com/nemtrif/ftest ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/CMakeLists.txt�����������������������������������������������������������0000664�0000000�0000000�00000003605�14411236400�0020117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������cmake_minimum_required (VERSION 3.0.2) project (utf8cpp VERSION 3.2.2 LANGUAGES CXX) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(IS_ROOT_PROJECT ON) else() set(IS_ROOT_PROJECT OFF) endif() option(UTF8_TESTS "Enable tests for UTF8-CPP" ${IS_ROOT_PROJECT}) option(UTF8_INSTALL "Enable installation for UTF8-CPP" ${IS_ROOT_PROJECT}) option(UTF8_SAMPLES "Enable building samples for UTF8-CPP" ${IS_ROOT_PROJECT}) add_library(utf8cpp INTERFACE) target_include_directories(utf8cpp INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/source>" $<INSTALL_INTERFACE:include/utf8cpp> ) add_library(utf8::cpp ALIAS utf8cpp) if(UTF8_INSTALL) include(CMakePackageConfigHelpers) if(MSVC) set(DEF_INSTALL_CMAKE_DIR CMake) else() include(GNUInstallDirs) # define CMAKE_INSTALL_* set(DEF_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/utf8cpp) endif() write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/utf8cppConfigVersion.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ) configure_package_config_file( ${PROJECT_SOURCE_DIR}/utf8cppConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/utf8cppConfig.cmake INSTALL_DESTINATION ${DEF_INSTALL_CMAKE_DIR} ) install(DIRECTORY source/ DESTINATION include/utf8cpp) install(TARGETS utf8cpp EXPORT utf8cppTargets) install(EXPORT utf8cppTargets DESTINATION ${DEF_INSTALL_CMAKE_DIR}) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/utf8cppConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/utf8cppConfigVersion.cmake DESTINATION ${DEF_INSTALL_CMAKE_DIR} ) endif() if(UTF8_SAMPLES) add_executable(docsample ${PROJECT_SOURCE_DIR}/samples/docsample.cpp) target_link_libraries(docsample PRIVATE utf8::cpp) endif() if(UTF8_TESTS) enable_testing() add_subdirectory(tests) endif() ���������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/LICENSE������������������������������������������������������������������0000664�0000000�0000000�00000002472�14411236400�0016365�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/README.md����������������������������������������������������������������0000664�0000000�0000000�00000154656�14411236400�0016653�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# UTF8-CPP: UTF-8 with C++ in a Portable Way ## Introduction C++ developers miss an easy and portable way of handling Unicode encoded strings. The original C++ Standard (known as C++98 or C++03) is Unicode agnostic. C++11 provides some support for Unicode on core language and library level: u8, u, and U character and string literals, char16_t and char32_t character types, u16string and u32string library classes, and codecvt support for conversions between Unicode encoding forms. In the meantime, developers use third party libraries like ICU, OS specific capabilities, or simply roll out their own solutions. In order to easily handle UTF-8 encoded Unicode strings, I came up with a small, C++98 compatible generic library. For anybody used to work with STL algorithms and iterators, it should be easy and natural to use. The code is freely available for any purpose - check out the [license](./LICENSE). The library has been used a lot in the past ten years both in commercial and open-source projects and is considered feature-complete now. If you run into bugs or performance issues, please let me know and I'll do my best to address them. The purpose of this article is not to offer an introduction to Unicode in general, and UTF-8 in particular. If you are not familiar with Unicode, be sure to check out [Unicode Home Page](http://www.unicode.org/) or some other source of information for Unicode. Also, it is not my aim to advocate the use of UTF-8 encoded strings in C++ programs; if you want to handle UTF-8 encoded strings from C++, I am sure you have good reasons for it. ## Examples of use ### Introductionary Sample To illustrate the use of the library, let's start with a small but complete program that opens a file containing UTF-8 encoded text, reads it line by line, checks each line for invalid UTF-8 byte sequences, and converts it to UTF-16 encoding and back to UTF-8: ```cpp #include <fstream> #include <iostream> #include <string> #include <vector> #include "utf8.h" using namespace std; int main(int argc, char** argv) { if (argc != 2) { cout << "\nUsage: docsample filename\n"; return 0; } const char* test_file_path = argv[1]; // Open the test file (must be UTF-8 encoded) ifstream fs8(test_file_path); if (!fs8.is_open()) { cout << "Could not open " << test_file_path << endl; return 0; } unsigned line_count = 1; string line; // Play with all the lines in the file while (getline(fs8, line)) { // check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function) #if __cplusplus >= 201103L // C++ 11 or later auto end_it = utf8::find_invalid(line.begin(), line.end()); #else string::iterator end_it = utf8::find_invalid(line.begin(), line.end()); #endif // C++ 11 if (end_it != line.end()) { cout << "Invalid UTF-8 encoding detected at line " << line_count << "\n"; cout << "This part is fine: " << string(line.begin(), end_it) << "\n"; } // Get the line length (at least for the valid part) int length = utf8::distance(line.begin(), end_it); cout << "Length of line " << line_count << " is " << length << "\n"; // Convert it to utf-16 #if __cplusplus >= 201103L // C++ 11 or later u16string utf16line = utf8::utf8to16(line); #else vector<unsigned short> utf16line; utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line)); #endif // C++ 11 // And back to utf-8; #if __cplusplus >= 201103L // C++ 11 or later string utf8line = utf8::utf16to8(utf16line); #else string utf8line; utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line)); #endif // C++ 11 // Confirm that the conversion went OK: if (utf8line != string(line.begin(), end_it)) cout << "Error in UTF-16 conversion at line: " << line_count << "\n"; line_count++; } return 0; } ``` In the previous code sample, for each line we performed a detection of invalid UTF-8 sequences with `find_invalid`; the number of characters (more precisely - the number of Unicode code points, including the end of line and even BOM if there is one) in each line was determined with a use of `utf8::distance`; finally, we have converted each line to UTF-16 encoding with `utf8to16` and back to UTF-8 with `utf16to8`. Note a different pattern of usage for old compilers. For instance, this is how we convert a UTF-8 encoded string to a UTF-16 encoded one with a pre - C++11 compiler: ```cpp vector<unsigned short> utf16line; utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line)); ``` With a more modern compiler, the same operation would look like: ```cpp u16string utf16line = utf8::utf8to16(line); ``` If `__cplusplus` macro points to a C++ 11 or later, the library exposes API that takes into account C++ standard Unicode strings and move semantics. With an older compiler, it is still possible to use the same functionality, just in a little less convenient way In case you do not trust the `__cplusplus` macro or, for instance, do not want to include the C++ 11 helper functions even with a modern compiler, define `UTF_CPP_CPLUSPLUS` macro before including `utf8.h` and assign it a value for the standard you want to use - the values are the same as for the `__cplusplus` macro. This can be also useful with compilers that are conservative in setting the `__cplusplus` macro even if they have a good support for a recent standard edition - Microsoft's Visual C++ is one example. ### Checking if a file contains valid UTF-8 text Here is a function that checks whether the content of a file is valid UTF-8 encoded text without reading the content into the memory: ```cpp bool valid_utf8_file(const char* file_name) { ifstream ifs(file_name); if (!ifs) return false; // even better, throw here istreambuf_iterator<char> it(ifs.rdbuf()); istreambuf_iterator<char> eos; return utf8::is_valid(it, eos); } ``` Because the function `utf8::is_valid()` works with input iterators, we were able to pass an `istreambuf_iterator` to `it` and read the content of the file directly without loading it to the memory first. Note that other functions that take input iterator arguments can be used in a similar way. For instance, to read the content of a UTF-8 encoded text file and convert the text to UTF-16, just do something like: ```cpp utf8::utf8to16(it, eos, back_inserter(u16string)); ``` ### Ensure that a string contains valid UTF-8 text If we have some text that "probably" contains UTF-8 encoded text and we want to replace any invalid UTF-8 sequence with a replacement character, something like the following function may be used: ```cpp void fix_utf8_string(std::string& str) { std::string temp; utf8::replace_invalid(str.begin(), str.end(), back_inserter(temp)); str = temp; } ``` The function will replace any invalid UTF-8 sequence with a Unicode replacement character. There is an overloaded function that enables the caller to supply their own replacement character. ## Points of interest #### Design goals and decisions The library was designed to be: 1. Generic: for better or worse, there are many C++ string classes out there, and the library should work with as many of them as possible. 2. Portable: the library should be portable both accross different platforms and compilers. The only non-portable code is a small section that declares unsigned integers of different sizes: three typedefs. They can be changed by the users of the library if they don't match their platform. The default setting should work for Windows (both 32 and 64 bit), and most 32 bit and 64 bit Unix derivatives. Support for post C++03 language features is included for modern compilers at API level only, so the library should work even with pretty old compilers. 3. Lightweight: follow the "pay only for what you use" guideline. 4. Unintrusive: avoid forcing any particular design or even programming style on the user. This is a library, not a framework. #### Alternatives In case you want to look into other means of working with UTF-8 strings from C++, here is the list of solutions I am aware of: 1. [ICU Library](http://icu.sourceforge.net/). It is very powerful, complete, feature-rich, mature, and widely used. Also big, intrusive, non-generic, and doesn't play well with the Standard Library. I definitelly recommend looking at ICU even if you don't plan to use it. 2. C++11 language and library features. Still far from complete, and not easy to use. 3. [Glib::ustring](http://www.gtkmm.org/gtkmm2/docs/tutorial/html/ch03s04.html). A class specifically made to work with UTF-8 strings, and also feel like `std::string`. If you prefer to have yet another string class in your code, it may be worth a look. Be aware of the licensing issues, though. 4. Platform dependent solutions: Windows and POSIX have functions to convert strings from one encoding to another. That is only a subset of what my library offers, but if that is all you need it may be good enough. ## Reference ### Functions From utf8 Namespace #### utf8::append Available in version 3.0 and later. Requires a C++ 11 compliant compiler. Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence to a UTF-8 string. ```cpp void append(char32_t cp, std::string& s); ``` `cp`: a code point to append to the string. `s`: a utf-8 encoded string to append the code point to. Example of use: ```cpp std::string u; append(0x0448, u); assert (u[0] == char(0xd1) && u[1] == char(0x88) && u.length() == 2); ``` In case of an invalid code point, a `utf8::invalid_code_point` exception is thrown. #### utf8::append Available in version 1.0 and later. Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence to a UTF-8 string. ```cpp template <typename octet_iterator> octet_iterator append(uint32_t cp, octet_iterator result); ``` `octet_iterator`: an output iterator. `cp`: a 32 bit integer representing a code point to append to the sequence. `result`: an output iterator to the place in the sequence where to append the code point. Return value: an iterator pointing to the place after the newly appended sequence. Example of use: ```cpp unsigned char u[5] = {0,0,0,0,0}; unsigned char* end = append(0x0448, u); assert (u[0] == 0xd1 && u[1] == 0x88 && u[2] == 0 && u[3] == 0 && u[4] == 0); ``` Note that `append` does not allocate any memory - it is the burden of the caller to make sure there is enough memory allocated for the operation. To make things more interesting, `append` can add anywhere between 1 and 4 octets to the sequence. In practice, you would most often want to use `std::back_inserter` to ensure that the necessary memory is allocated. In case of an invalid code point, a `utf8::invalid_code_point` exception is thrown. #### utf8::next Available in version 1.0 and later. Given the iterator to the beginning of the UTF-8 sequence, it returns the code point and moves the iterator to the next position. ```cpp template <typename octet_iterator> uint32_t next(octet_iterator& it, octet_iterator end); ``` `octet_iterator`: an input iterator. `it`: a reference to an iterator pointing to the beginning of an UTF-8 encoded code point. After the function returns, it is incremented to point to the beginning of the next code point. `end`: end of the UTF-8 sequence to be processed. If `it` gets equal to `end` during the extraction of a code point, an `utf8::not_enough_room` exception is thrown. Return value: the 32 bit representation of the processed UTF-8 code point. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; char* w = twochars; int cp = next(w, twochars + 6); assert (cp == 0x65e5); assert (w == twochars + 3); ``` This function is typically used to iterate through a UTF-8 encoded string. In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. #### utf8::peek_next Available in version 2.1 and later. Given the iterator to the beginning of the UTF-8 sequence, it returns the code point for the following sequence without changing the value of the iterator. ```cpp template <typename octet_iterator> uint32_t peek_next(octet_iterator it, octet_iterator end); ``` `octet_iterator`: an input iterator. `it`: an iterator pointing to the beginning of an UTF-8 encoded code point. `end`: end of the UTF-8 sequence to be processed. If `it` gets equal to `end` during the extraction of a code point, an `utf8::not_enough_room` exception is thrown. Return value: the 32 bit representation of the processed UTF-8 code point. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; char* w = twochars; int cp = peek_next(w, twochars + 6); assert (cp == 0x65e5); assert (w == twochars); ``` In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. #### utf8::prior Available in version 1.02 and later. Given a reference to an iterator pointing to an octet in a UTF-8 sequence, it decreases the iterator until it hits the beginning of the previous UTF-8 encoded code point and returns the 32 bits representation of the code point. ```cpp template <typename octet_iterator> uint32_t prior(octet_iterator& it, octet_iterator start); ``` `octet_iterator`: a bidirectional iterator. `it`: a reference pointing to an octet within a UTF-8 encoded string. After the function returns, it is decremented to point to the beginning of the previous code point. `start`: an iterator to the beginning of the sequence where the search for the beginning of a code point is performed. It is a safety measure to prevent passing the beginning of the string in the search for a UTF-8 lead octet. Return value: the 32 bit representation of the previous code point. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; unsigned char* w = twochars + 3; int cp = prior (w, twochars); assert (cp == 0x65e5); assert (w == twochars); ``` This function has two purposes: one is two iterate backwards through a UTF-8 encoded string. Note that it is usually a better idea to iterate forward instead, since `utf8::next` is faster. The second purpose is to find a beginning of a UTF-8 sequence if we have a random position within a string. Note that in that case `utf8::prior` may not detect an invalid UTF-8 sequence in some scenarios: for instance if there are superfluous trail octets, it will just skip them. `it` will typically point to the beginning of a code point, and `start` will point to the beginning of the string to ensure we don't go backwards too far. `it` is decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence beginning with that octet is decoded to a 32 bit representation and returned. In case `start` is reached before a UTF-8 lead octet is hit, or if an invalid UTF-8 sequence is started by the lead octet, an `invalid_utf8` exception is thrown. In case `start` equals `it`, a `not_enough_room` exception is thrown. #### utf8::advance Available in version 1.0 and later. Advances an iterator by the specified number of code points within an UTF-8 sequence. ```cpp template <typename octet_iterator, typename distance_type> void advance (octet_iterator& it, distance_type n, octet_iterator end); ``` `octet_iterator`: an input iterator. `distance_type`: an integral type convertible to `octet_iterator`'s difference type. `it`: a reference to an iterator pointing to the beginning of an UTF-8 encoded code point. After the function returns, it is incremented to point to the nth following code point. `n`: number of code points `it` should be advanced. A negative value means decrement. `end`: limit of the UTF-8 sequence to be processed. If `n` is positive and `it` gets equal to `end` during the extraction of a code point, an `utf8::not_enough_room` exception is thrown. If `n` is negative and `it` reaches `end` while `it` points t a trail byte of a UTF-8 sequence, a `utf8::invalid_code_point` exception is thrown. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; unsigned char* w = twochars; advance (w, 2, twochars + 6); assert (w == twochars + 5); advance (w, -2, twochars); assert (w == twochars); ``` In case of an invalid code point, a `utf8::invalid_code_point` exception is thrown. #### utf8::distance Available in version 1.0 and later. Given the iterators to two UTF-8 encoded code points in a seqence, returns the number of code points between them. ```cpp template <typename octet_iterator> typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last); ``` `octet_iterator`: an input iterator. `first`: an iterator to a beginning of a UTF-8 encoded code point. `last`: an iterator to a "post-end" of the last UTF-8 encoded code point in the sequence we are trying to determine the length. It can be the beginning of a new code point, or not. Return value the distance between the iterators, in code points. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; size_t dist = utf8::distance(twochars, twochars + 5); assert (dist == 2); ``` This function is used to find the length (in code points) of a UTF-8 encoded string. The reason it is called _distance_, rather than, say, _length_ is mainly because developers are used that _length_ is an O(1) function. Computing the length of an UTF-8 string is a linear operation, and it looked better to model it after `std::distance` algorithm. In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. If `last` does not point to the past-of-end of a UTF-8 seqence, a `utf8::not_enough_room` exception is thrown. #### utf8::utf16to8 Available in version 3.0 and later. Requires a C++ 11 compliant compiler. Converts a UTF-16 encoded string to UTF-8. ```cpp std::string utf16to8(const std::u16string& s); ``` `s`: a UTF-16 encoded string. Return value: A UTF-8 encoded string. Example of use: ```cpp u16string utf16string = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; string u = utf16to8(utf16string); assert (u.size() == 10); ``` In case of invalid UTF-16 sequence, a `utf8::invalid_utf16` exception is thrown. #### utf8::utf16to8 Available in version 3.2 and later. Requires a C++ 17 compliant compiler. Converts a UTF-16 encoded string to UTF-8. ```cpp std::string utf16to8(std::u16string_view s); ``` `s`: a UTF-16 encoded string. Return value: A UTF-8 encoded string. Example of use: ```cpp u16string utf16string = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; u16string_view utf16stringview(u16string); string u = utf16to8(utf16string); assert (u.size() == 10); ``` In case of invalid UTF-16 sequence, a `utf8::invalid_utf16` exception is thrown. #### utf8::utf16to8 Available in version 1.0 and later. Converts a UTF-16 encoded string to UTF-8. ```cpp template <typename u16bit_iterator, typename octet_iterator> octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result); ``` `u16bit_iterator`: an input iterator. `octet_iterator`: an output iterator. `start`: an iterator pointing to the beginning of the UTF-16 encoded string to convert. `end`: an iterator pointing to pass-the-end of the UTF-16 encoded string to convert. `result`: an output iterator to the place in the UTF-8 string where to append the result of conversion. Return value: An iterator pointing to the place after the appended UTF-8 string. Example of use: ```cpp unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; vector<unsigned char> utf8result; utf16to8(utf16string, utf16string + 5, back_inserter(utf8result)); assert (utf8result.size() == 10); ``` In case of invalid UTF-16 sequence, a `utf8::invalid_utf16` exception is thrown. #### utf8::utf8to16 Available in version 3.0 and later. Requires a C++ 11 compliant compiler. Converts an UTF-8 encoded string to UTF-16. ```cpp std::u16string utf8to16(const std::string& s); ``` `s`: an UTF-8 encoded string to convert. Return value: A UTF-16 encoded string Example of use: ```cpp string utf8_with_surrogates = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; u16string utf16result = utf8to16(utf8_with_surrogates); assert (utf16result.length() == 4); assert (utf16result[2] == 0xd834); assert (utf16result[3] == 0xdd1e); ``` In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. #### utf8::utf8to16 Available in version 3.2 and later. Requires a C++ 17 compliant compiler. Converts an UTF-8 encoded string to UTF-16. ```cpp std::u16string utf8to16(std::string_view s); ``` `s`: an UTF-8 encoded string to convert. Return value: A UTF-16 encoded string Example of use: ```cpp string_view utf8_with_surrogates = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; u16string utf16result = utf8to16(utf8_with_surrogates); assert (utf16result.length() == 4); assert (utf16result[2] == 0xd834); assert (utf16result[3] == 0xdd1e); ``` In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. #### utf8::utf8to16 Available in version 1.0 and later. Converts an UTF-8 encoded string to UTF-16 ```cpp template <typename u16bit_iterator, typename octet_iterator> u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result); ``` `octet_iterator`: an input iterator. `u16bit_iterator`: an output iterator. `start`: an iterator pointing to the beginning of the UTF-8 encoded string to convert. < br /> `end`: an iterator pointing to pass-the-end of the UTF-8 encoded string to convert. `result`: an output iterator to the place in the UTF-16 string where to append the result of conversion. Return value: An iterator pointing to the place after the appended UTF-16 string. Example of use: ```cpp char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; vector <unsigned short> utf16result; utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result)); assert (utf16result.size() == 4); assert (utf16result[2] == 0xd834); assert (utf16result[3] == 0xdd1e); ``` In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. If `end` does not point to the past-of-end of a UTF-8 seqence, a `utf8::not_enough_room` exception is thrown. #### utf8::utf32to8 Available in version 3.0 and later. Requires a C++ 11 compliant compiler. Converts a UTF-32 encoded string to UTF-8. ```cpp std::string utf32to8(const std::u32string& s); ``` `s`: a UTF-32 encoded string. Return value: a UTF-8 encoded string. Example of use: ```cpp u32string utf32string = {0x448, 0x65E5, 0x10346}; string utf8result = utf32to8(utf32string); assert (utf8result.size() == 9); ``` In case of invalid UTF-32 string, a `utf8::invalid_code_point` exception is thrown. #### utf8::utf32to8 Available in version 3.2 and later. Requires a C++ 17 compliant compiler. Converts a UTF-32 encoded string to UTF-8. ```cpp std::string utf32to8(std::u32string_view s); ``` `s`: a UTF-32 encoded string. Return value: a UTF-8 encoded string. Example of use: ```cpp u32string utf32string = {0x448, 0x65E5, 0x10346}; u32string_view utf32stringview(utf32string); string utf8result = utf32to8(utf32stringview); assert (utf8result.size() == 9); ``` In case of invalid UTF-32 string, a `utf8::invalid_code_point` exception is thrown. #### utf8::utf32to8 Available in version 1.0 and later. Converts a UTF-32 encoded string to UTF-8. ```cpp template <typename octet_iterator, typename u32bit_iterator> octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result); ``` `octet_iterator`: an output iterator. `u32bit_iterator`: an input iterator. `start`: an iterator pointing to the beginning of the UTF-32 encoded string to convert. `end`: an iterator pointing to pass-the-end of the UTF-32 encoded string to convert. `result`: an output iterator to the place in the UTF-8 string where to append the result of conversion. Return value: An iterator pointing to the place after the appended UTF-8 string. Example of use: ```cpp int utf32string[] = {0x448, 0x65E5, 0x10346, 0}; vector<unsigned char> utf8result; utf32to8(utf32string, utf32string + 3, back_inserter(utf8result)); assert (utf8result.size() == 9); ``` In case of invalid UTF-32 string, a `utf8::invalid_code_point` exception is thrown. #### utf8::utf8to32 Available in version 3.0 and later. Requires a C++ 11 compliant compiler. Converts a UTF-8 encoded string to UTF-32. ```cpp std::u32string utf8to32(const std::string& s); ``` `s`: a UTF-8 encoded string. Return value: a UTF-32 encoded string. Example of use: ```cpp const char* twochars = "\xe6\x97\xa5\xd1\x88"; u32string utf32result = utf8to32(twochars); assert (utf32result.size() == 2); ``` In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. #### utf8::utf8to32 Available in version 3.2 and later. Requires a C++ 17 compliant compiler. Converts a UTF-8 encoded string to UTF-32. ```cpp std::u32string utf8to32(std::string_view s); ``` `s`: a UTF-8 encoded string. Return value: a UTF-32 encoded string. Example of use: ```cpp string_view twochars = "\xe6\x97\xa5\xd1\x88"; u32string utf32result = utf8to32(twochars); assert (utf32result.size() == 2); ``` In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. #### utf8::utf8to32 Available in version 1.0 and later. Converts a UTF-8 encoded string to UTF-32. ```cpp template <typename octet_iterator, typename u32bit_iterator> u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result); ``` `octet_iterator`: an input iterator. `u32bit_iterator`: an output iterator. `start`: an iterator pointing to the beginning of the UTF-8 encoded string to convert. `end`: an iterator pointing to pass-the-end of the UTF-8 encoded string to convert. `result`: an output iterator to the place in the UTF-32 string where to append the result of conversion. Return value: An iterator pointing to the place after the appended UTF-32 string. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; vector<int> utf32result; utf8to32(twochars, twochars + 5, back_inserter(utf32result)); assert (utf32result.size() == 2); ``` In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. If `end` does not point to the past-of-end of a UTF-8 seqence, a `utf8::not_enough_room` exception is thrown. #### utf8::find_invalid Available in version 3.0 and later. Requires a C++ 11 compliant compiler. Detects an invalid sequence within a UTF-8 string. ```cpp std::size_t find_invalid(const std::string& s); ``` `s`: a UTF-8 encoded string. Return value: the index of the first invalid octet in the UTF-8 string. In case none were found, equals `std::string::npos`. Example of use: ```cpp string utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; auto invalid = find_invalid(utf_invalid); assert (invalid == 5); ``` This function is typically used to make sure a UTF-8 string is valid before processing it with other functions. It is especially important to call it if before doing any of the _unchecked_ operations on it. #### utf8::find_invalid Available in version 3.2 and later. Requires a C++ 17 compliant compiler. Detects an invalid sequence within a UTF-8 string. ```cpp std::size_t find_invalid(std::string_view s); ``` `s`: a UTF-8 encoded string. Return value: the index of the first invalid octet in the UTF-8 string. In case none were found, equals `std::string_view::npos`. Example of use: ```cpp string_view utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; auto invalid = find_invalid(utf_invalid); assert (invalid == 5); ``` This function is typically used to make sure a UTF-8 string is valid before processing it with other functions. It is especially important to call it if before doing any of the _unchecked_ operations on it. #### utf8::find_invalid Available in version 1.0 and later. Detects an invalid sequence within a UTF-8 string. ```cpp template <typename octet_iterator> octet_iterator find_invalid(octet_iterator start, octet_iterator end); ``` `octet_iterator`: an input iterator. `start`: an iterator pointing to the beginning of the UTF-8 string to test for validity. `end`: an iterator pointing to pass-the-end of the UTF-8 string to test for validity. Return value: an iterator pointing to the first invalid octet in the UTF-8 string. In case none were found, equals `end`. Example of use: ```cpp char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa"; char* invalid = find_invalid(utf_invalid, utf_invalid + 6); assert (invalid == utf_invalid + 5); ``` This function is typically used to make sure a UTF-8 string is valid before processing it with other functions. It is especially important to call it if before doing any of the _unchecked_ operations on it. #### utf8::is_valid Available in version 3.0 and later. Requires a C++ 11 compliant compiler. Checks whether a string object contains valid UTF-8 encoded text. ```cpp bool is_valid(const std::string& s); ``` `s`: a UTF-8 encoded string. Return value: `true` if the string contains valid UTF-8 encoded text; `false` if not. Example of use: ```cpp char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa"; bool bvalid = is_valid(utf_invalid); assert (bvalid == false); ``` You may want to use `is_valid` to make sure that a string contains valid UTF-8 text without the need to know where it fails if it is not valid. #### utf8::is_valid Available in version 3.2 and later. Requires a C++ 17 compliant compiler. Checks whether a string object contains valid UTF-8 encoded text. ```cpp bool is_valid(std::string_view s); ``` `s`: a UTF-8 encoded string. Return value: `true` if the string contains valid UTF-8 encoded text; `false` if not. Example of use: ```cpp string_view utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; bool bvalid = is_valid(utf_invalid); assert (bvalid == false); ``` You may want to use `is_valid` to make sure that a string contains valid UTF-8 text without the need to know where it fails if it is not valid. #### utf8::is_valid Available in version 1.0 and later. Checks whether a sequence of octets is a valid UTF-8 string. ```cpp template <typename octet_iterator> bool is_valid(octet_iterator start, octet_iterator end); ``` `octet_iterator`: an input iterator. `start`: an iterator pointing to the beginning of the UTF-8 string to test for validity. `end`: an iterator pointing to pass-the-end of the UTF-8 string to test for validity. Return value: `true` if the sequence is a valid UTF-8 string; `false` if not. Example of use: ```cpp char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa"; bool bvalid = is_valid(utf_invalid, utf_invalid + 6); assert (bvalid == false); ``` `is_valid` is a shorthand for `find_invalid(start, end) == end;`. You may want to use it to make sure that a byte seqence is a valid UTF-8 string without the need to know where it fails if it is not valid. #### utf8::replace_invalid Available in version 3.0 and later. Requires a C++ 11 compliant compiler. Replaces all invalid UTF-8 sequences within a string with a replacement marker. ```cpp std::string replace_invalid(const std::string& s, char32_t replacement); std::string replace_invalid(const std::string& s); ``` `s`: a UTF-8 encoded string. `replacement`: A Unicode code point for the replacement marker. The version without this parameter assumes the value `0xfffd` Return value: A UTF-8 encoded string with replaced invalid sequences. Example of use: ```cpp string invalid_sequence = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; string replace_invalid_result = replace_invalid(invalid_sequence, '?'); bvalid = is_valid(replace_invalid_result); assert (bvalid); const string fixed_invalid_sequence = "a????z"; assert (fixed_invalid_sequence == replace_invalid_result); ``` #### utf8::replace_invalid Available in version 3.2 and later. Requires a C++ 17 compliant compiler. Replaces all invalid UTF-8 sequences within a string with a replacement marker. ```cpp std::string replace_invalid(std::string_view s, char32_t replacement); std::string replace_invalid(std::string_view s); ``` `s`: a UTF-8 encoded string. `replacement`: A Unicode code point for the replacement marker. The version without this parameter assumes the value `0xfffd` Return value: A UTF-8 encoded string with replaced invalid sequences. Example of use: ```cpp string_view invalid_sequence = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; string replace_invalid_result = replace_invalid(invalid_sequence, '?'); bool bvalid = is_valid(replace_invalid_result); assert (bvalid); const string fixed_invalid_sequence = "a????z"; assert(fixed_invalid_sequence, replace_invalid_result); ``` #### utf8::replace_invalid Available in version 2.0 and later. Replaces all invalid UTF-8 sequences within a string with a replacement marker. ```cpp template <typename octet_iterator, typename output_iterator> output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement); template <typename octet_iterator, typename output_iterator> output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out); ``` `octet_iterator`: an input iterator. `output_iterator`: an output iterator. `start`: an iterator pointing to the beginning of the UTF-8 string to look for invalid UTF-8 sequences. `end`: an iterator pointing to pass-the-end of the UTF-8 string to look for invalid UTF-8 sequences. `out`: An output iterator to the range where the result of replacement is stored. `replacement`: A Unicode code point for the replacement marker. The version without this parameter assumes the value `0xfffd` Return value: An iterator pointing to the place after the UTF-8 string with replaced invalid sequences. Example of use: ```cpp char invalid_sequence[] = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; vector<char> replace_invalid_result; replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), '?'); bvalid = is_valid(replace_invalid_result.begin(), replace_invalid_result.end()); assert (bvalid); char* fixed_invalid_sequence = "a????z"; assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence)); ``` `replace_invalid` does not perform in-place replacement of invalid sequences. Rather, it produces a copy of the original string with the invalid sequences replaced with a replacement marker. Therefore, `out` must not be in the `[start, end]` range. #### utf8::starts_with_bom Available in version 3.0 and later. Requires a C++ 11 compliant compiler. Checks whether a string starts with a UTF-8 byte order mark (BOM) ```cpp bool starts_with_bom(const std::string& s); ``` `s`: a UTF-8 encoded string. Return value: `true` if the string starts with a UTF-8 byte order mark; `false` if not. Example of use: ```cpp string byte_order_mark = {char(0xef), char(0xbb), char(0xbf)}; bool bbom = starts_with_bom(byte_order_mark); assert (bbom == true); string threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; bool no_bbom = starts_with_bom(threechars); assert (no_bbom == false); ``` The typical use of this function is to check the first three bytes of a file. If they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 encoded text. #### utf8::starts_with_bom Available in version 3.2 and later. Requires a C++ 17 compliant compiler. Checks whether a string starts with a UTF-8 byte order mark (BOM) ```cpp bool starts_with_bom(std::string_view s); ``` `s`: a UTF-8 encoded string. Return value: `true` if the string starts with a UTF-8 byte order mark; `false` if not. Example of use: ```cpp string byte_order_mark = {char(0xef), char(0xbb), char(0xbf)}; string_view byte_order_mark_view(byte_order_mark); bool bbom = starts_with_bom(byte_order_mark_view); assert (bbom); string_view threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; bool no_bbom = starts_with_bom(threechars); assert (!no_bbom); ``` The typical use of this function is to check the first three bytes of a file. If they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 encoded text. #### utf8::starts_with_bom Available in version 2.3 and later. Checks whether an octet sequence starts with a UTF-8 byte order mark (BOM) ```cpp template <typename octet_iterator> bool starts_with_bom (octet_iterator it, octet_iterator end); ``` `octet_iterator`: an input iterator. `it`: beginning of the octet sequence to check `end`: pass-end of the sequence to check Return value: `true` if the sequence starts with a UTF-8 byte order mark; `false` if not. Example of use: ```cpp unsigned char byte_order_mark[] = {0xef, 0xbb, 0xbf}; bool bbom = starts_with_bom(byte_order_mark, byte_order_mark + sizeof(byte_order_mark)); assert (bbom == true); ``` The typical use of this function is to check the first three bytes of a file. If they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 encoded text. ### Types From utf8 Namespace #### utf8::exception Available in version 2.3 and later. Base class for the exceptions thrown by UTF CPP library functions. ```cpp class exception : public std::exception {}; ``` Example of use: ```cpp try { code_that_uses_utf_cpp_library(); } catch(const utf8::exception& utfcpp_ex) { cerr << utfcpp_ex.what(); } ``` #### utf8::invalid_code_point Available in version 1.0 and later. Thrown by UTF8 CPP functions such as `advance` and `next` if an UTF-8 sequence represents and invalid code point. ```cpp class invalid_code_point : public exception { public: uint32_t code_point() const; }; ``` Member function `code_point()` can be used to determine the invalid code point that caused the exception to be thrown. #### utf8::invalid_utf8 Available in version 1.0 and later. Thrown by UTF8 CPP functions such as `next` and `prior` if an invalid UTF-8 sequence is detected during decoding. ```cpp class invalid_utf8 : public exception { public: uint8_t utf8_octet() const; }; ``` Member function `utf8_octet()` can be used to determine the beginning of the byte sequence that caused the exception to be thrown. #### utf8::invalid_utf16 Available in version 1.0 and later. Thrown by UTF8 CPP function `utf16to8` if an invalid UTF-16 sequence is detected during decoding. ```cpp class invalid_utf16 : public exception { public: uint16_t utf16_word() const; }; ``` Member function `utf16_word()` can be used to determine the UTF-16 code unit that caused the exception to be thrown. #### utf8::not_enough_room Available in version 1.0 and later. Thrown by UTF8 CPP functions such as `next` if the end of the decoded UTF-8 sequence was reached before the code point was decoded. ```cpp class not_enough_room : public exception {}; ``` #### utf8::iterator Available in version 2.0 and later. Adapts the underlying octet iterator to iterate over the sequence of code points, rather than raw octets. ```cpp template <typename octet_iterator> class iterator; ``` ##### Member functions `iterator();` the deafult constructor; the underlying octet_iterator is constructed with its default constructor. `explicit iterator (const octet_iterator& octet_it, const octet_iterator& range_start, const octet_iterator& range_end);` a constructor that initializes the underlying octet_iterator with octet_it and sets the range in which the iterator is considered valid. `octet_iterator base () const;` returns the underlying octet_iterator. `uint32_t operator * () const;` decodes the utf-8 sequence the underlying octet_iterator is pointing to and returns the code point. `bool operator == (const iterator& rhs) const;` returns `true` if the two underlaying iterators are equal. `bool operator != (const iterator& rhs) const;` returns `true` if the two underlaying iterators are not equal. `iterator& operator ++ ();` the prefix increment - moves the iterator to the next UTF-8 encoded code point. `iterator operator ++ (int);` the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. `iterator& operator -- ();` the prefix decrement - moves the iterator to the previous UTF-8 encoded code point. `iterator operator -- (int);` the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. Example of use: ```cpp char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; utf8::iterator<char*> it(threechars, threechars, threechars + 9); utf8::iterator<char*> it2 = it; assert (it2 == it); assert (*it == 0x10346); assert (*(++it) == 0x65e5); assert ((*it++) == 0x65e5); assert (*it == 0x0448); assert (it != it2); utf8::iterator<char*> endit (threechars + 9, threechars, threechars + 9); assert (++it == endit); assert (*(--it) == 0x0448); assert ((*it--) == 0x0448); assert (*it == 0x65e5); assert (--it == utf8::iterator<char*>(threechars, threechars, threechars + 9)); assert (*it == 0x10346); ``` The purpose of `utf8::iterator` adapter is to enable easy iteration as well as the use of STL algorithms with UTF-8 encoded strings. Increment and decrement operators are implemented in terms of `utf8::next()` and `utf8::prior()` functions. Note that `utf8::iterator` adapter is a checked iterator. It operates on the range specified in the constructor; any attempt to go out of that range will result in an exception. Even the comparison operators require both iterator object to be constructed against the same range - otherwise an exception is thrown. Typically, the range will be determined by sequence container functions `begin` and `end`, i.e.: ```cpp std::string s = "example"; utf8::iterator i (s.begin(), s.begin(), s.end()); ``` ### Functions From utf8::unchecked Namespace #### utf8::unchecked::append Available in version 1.0 and later. Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence to a UTF-8 string. ```cpp template <typename octet_iterator> octet_iterator append(uint32_t cp, octet_iterator result); ``` `cp`: A 32 bit integer representing a code point to append to the sequence. `result`: An output iterator to the place in the sequence where to append the code point. Return value: An iterator pointing to the place after the newly appended sequence. Example of use: ```cpp unsigned char u[5] = {0,0,0,0,0}; unsigned char* end = unchecked::append(0x0448, u); assert (u[0] == 0xd1 && u[1] == 0x88 && u[2] == 0 && u[3] == 0 && u[4] == 0); ``` This is a faster but less safe version of `utf8::append`. It does not check for validity of the supplied code point, and may produce an invalid UTF-8 sequence. #### utf8::unchecked::next Available in version 1.0 and later. Given the iterator to the beginning of a UTF-8 sequence, it returns the code point and moves the iterator to the next position. ```cpp template <typename octet_iterator> uint32_t next(octet_iterator& it); ``` `it`: a reference to an iterator pointing to the beginning of an UTF-8 encoded code point. After the function returns, it is incremented to point to the beginning of the next code point. Return value: the 32 bit representation of the processed UTF-8 code point. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; char* w = twochars; int cp = unchecked::next(w); assert (cp == 0x65e5); assert (w == twochars + 3); ``` This is a faster but less safe version of `utf8::next`. It does not check for validity of the supplied UTF-8 sequence. #### utf8::unchecked::peek_next Available in version 2.1 and later. Given the iterator to the beginning of a UTF-8 sequence, it returns the code point. ```cpp template <typename octet_iterator> uint32_t peek_next(octet_iterator it); ``` `it`: an iterator pointing to the beginning of an UTF-8 encoded code point. Return value: the 32 bit representation of the processed UTF-8 code point. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; char* w = twochars; int cp = unchecked::peek_next(w); assert (cp == 0x65e5); assert (w == twochars); ``` This is a faster but less safe version of `utf8::peek_next`. It does not check for validity of the supplied UTF-8 sequence. #### utf8::unchecked::prior Available in version 1.02 and later. Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it decreases the iterator until it hits the beginning of the previous UTF-8 encoded code point and returns the 32 bits representation of the code point. ```cpp template <typename octet_iterator> uint32_t prior(octet_iterator& it); ``` `it`: a reference pointing to an octet within a UTF-8 encoded string. After the function returns, it is decremented to point to the beginning of the previous code point. Return value: the 32 bit representation of the previous code point. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; char* w = twochars + 3; int cp = unchecked::prior (w); assert (cp == 0x65e5); assert (w == twochars); ``` This is a faster but less safe version of `utf8::prior`. It does not check for validity of the supplied UTF-8 sequence and offers no boundary checking. #### utf8::unchecked::advance Available in version 1.0 and later. Advances an iterator by the specified number of code points within an UTF-8 sequence. ```cpp template <typename octet_iterator, typename distance_type> void advance (octet_iterator& it, distance_type n); ``` `it`: a reference to an iterator pointing to the beginning of an UTF-8 encoded code point. After the function returns, it is incremented to point to the nth following code point. `n`: number of code points `it` should be advanced. A negative value means decrement. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; char* w = twochars; unchecked::advance (w, 2); assert (w == twochars + 5); ``` This is a faster but less safe version of `utf8::advance`. It does not check for validity of the supplied UTF-8 sequence and offers no boundary checking. #### utf8::unchecked::distance Available in version 1.0 and later. Given the iterators to two UTF-8 encoded code points in a seqence, returns the number of code points between them. ```cpp template <typename octet_iterator> typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last); ``` `first`: an iterator to a beginning of a UTF-8 encoded code point. `last`: an iterator to a "post-end" of the last UTF-8 encoded code point in the sequence we are trying to determine the length. It can be the beginning of a new code point, or not. Return value: the distance between the iterators, in code points. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; size_t dist = utf8::unchecked::distance(twochars, twochars + 5); assert (dist == 2); ``` This is a faster but less safe version of `utf8::distance`. It does not check for validity of the supplied UTF-8 sequence. #### utf8::unchecked::utf16to8 Available in version 1.0 and later. Converts a UTF-16 encoded string to UTF-8. ```cpp template <typename u16bit_iterator, typename octet_iterator> octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result); ``` `start`: an iterator pointing to the beginning of the UTF-16 encoded string to convert. `end`: an iterator pointing to pass-the-end of the UTF-16 encoded string to convert. `result`: an output iterator to the place in the UTF-8 string where to append the result of conversion. Return value: An iterator pointing to the place after the appended UTF-8 string. Example of use: ```cpp unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; vector<unsigned char> utf8result; unchecked::utf16to8(utf16string, utf16string + 5, back_inserter(utf8result)); assert (utf8result.size() == 10); ``` This is a faster but less safe version of `utf8::utf16to8`. It does not check for validity of the supplied UTF-16 sequence. #### utf8::unchecked::utf8to16 Available in version 1.0 and later. Converts an UTF-8 encoded string to UTF-16 ```cpp template <typename u16bit_iterator, typename octet_iterator> u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result); ``` `start`: an iterator pointing to the beginning of the UTF-8 encoded string to convert. < br /> `end`: an iterator pointing to pass-the-end of the UTF-8 encoded string to convert. `result`: an output iterator to the place in the UTF-16 string where to append the result of conversion. Return value: An iterator pointing to the place after the appended UTF-16 string. Example of use: ```cpp char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; vector <unsigned short> utf16result; unchecked::utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result)); assert (utf16result.size() == 4); assert (utf16result[2] == 0xd834); assert (utf16result[3] == 0xdd1e); ``` This is a faster but less safe version of `utf8::utf8to16`. It does not check for validity of the supplied UTF-8 sequence. #### utf8::unchecked::utf32to8 Available in version 1.0 and later. Converts a UTF-32 encoded string to UTF-8. ```cpp template <typename octet_iterator, typename u32bit_iterator> octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result); ``` `start`: an iterator pointing to the beginning of the UTF-32 encoded string to convert. `end`: an iterator pointing to pass-the-end of the UTF-32 encoded string to convert. `result`: an output iterator to the place in the UTF-8 string where to append the result of conversion. Return value: An iterator pointing to the place after the appended UTF-8 string. Example of use: ```cpp int utf32string[] = {0x448, 0x65e5, 0x10346, 0}; vector<unsigned char> utf8result; utf32to8(utf32string, utf32string + 3, back_inserter(utf8result)); assert (utf8result.size() == 9); ``` This is a faster but less safe version of `utf8::utf32to8`. It does not check for validity of the supplied UTF-32 sequence. #### utf8::unchecked::utf8to32 Available in version 1.0 and later. Converts a UTF-8 encoded string to UTF-32. ```cpp template <typename octet_iterator, typename u32bit_iterator> u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result); ``` `start`: an iterator pointing to the beginning of the UTF-8 encoded string to convert. `end`: an iterator pointing to pass-the-end of the UTF-8 encoded string to convert. `result`: an output iterator to the place in the UTF-32 string where to append the result of conversion. Return value: An iterator pointing to the place after the appended UTF-32 string. Example of use: ```cpp char* twochars = "\xe6\x97\xa5\xd1\x88"; vector<int> utf32result; unchecked::utf8to32(twochars, twochars + 5, back_inserter(utf32result)); assert (utf32result.size() == 2); ``` This is a faster but less safe version of `utf8::utf8to32`. It does not check for validity of the supplied UTF-8 sequence. #### utf8::unchecked::replace_invalid Available in version 3.1 and later. Replaces all invalid UTF-8 sequences within a string with a replacement marker. ```cpp template <typename octet_iterator, typename output_iterator> output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement); template <typename octet_iterator, typename output_iterator> output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out); ``` `octet_iterator`: an input iterator. `output_iterator`: an output iterator. `start`: an iterator pointing to the beginning of the UTF-8 string to look for invalid UTF-8 sequences. `end`: an iterator pointing to pass-the-end of the UTF-8 string to look for invalid UTF-8 sequences. `out`: An output iterator to the range where the result of replacement is stored. `replacement`: A Unicode code point for the replacement marker. The version without this parameter assumes the value `0xfffd` Return value: An iterator pointing to the place after the UTF-8 string with replaced invalid sequences. Example of use: ```cpp char invalid_sequence[] = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; vector<char> replace_invalid_result; unchecked::replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), '?'); bvalid = utf8::is_valid(replace_invalid_result.begin(), replace_invalid_result.end()); assert (bvalid); char* fixed_invalid_sequence = "a????z"; assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence)); ``` `replace_invalid` does not perform in-place replacement of invalid sequences. Rather, it produces a copy of the original string with the invalid sequences replaced with a replacement marker. Therefore, `out` must not be in the `[start, end]` range. Unlike `utf8::replace_invalid`, this function does not verify validity of the replacement marker. ### Types From utf8::unchecked Namespace #### utf8::iterator Available in version 2.0 and later. Adapts the underlying octet iterator to iterate over the sequence of code points, rather than raw octets. ```cpp template <typename octet_iterator> class iterator; ``` ##### Member functions `iterator();` the deafult constructor; the underlying octet_iterator is constructed with its default constructor. `explicit iterator (const octet_iterator& octet_it);` a constructor that initializes the underlying octet_iterator with `octet_it`. `octet_iterator base () const;` returns the underlying octet_iterator. `uint32_t operator * () const;` decodes the utf-8 sequence the underlying octet_iterator is pointing to and returns the code point. `bool operator == (const iterator& rhs) const;` returns `true` if the two underlaying iterators are equal. `bool operator != (const iterator& rhs) const;` returns `true` if the two underlaying iterators are not equal. `iterator& operator ++ ();` the prefix increment - moves the iterator to the next UTF-8 encoded code point. `iterator operator ++ (int);` the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. `iterator& operator -- ();` the prefix decrement - moves the iterator to the previous UTF-8 encoded code point. `iterator operator -- (int);` the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. Example of use: ```cpp char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; utf8::unchecked::iterator<char*> un_it(threechars); utf8::unchecked::iterator<char*> un_it2 = un_it; assert (un_it2 == un_it); assert (*un_it == 0x10346); assert (*(++un_it) == 0x65e5); assert ((*un_it++) == 0x65e5); assert (*un_it == 0x0448); assert (un_it != un_it2); utf8::::unchecked::iterator<char*> un_endit (threechars + 9); assert (++un_it == un_endit); assert (*(--un_it) == 0x0448); assert ((*un_it--) == 0x0448); assert (*un_it == 0x65e5); assert (--un_it == utf8::unchecked::iterator<char*>(threechars)); assert (*un_it == 0x10346); ``` This is an unchecked version of `utf8::iterator`. It is faster in many cases, but offers no validity or range checks. ## Links 1. [The Unicode Consortium](http://www.unicode.org/). 2. [ICU Library](http://icu.sourceforge.net/). 3. [UTF-8 at Wikipedia](http://en.wikipedia.org/wiki/UTF-8) 4. [UTF-8 and Unicode FAQ for Unix/Linux](http://www.cl.cam.ac.uk/~mgk25/unicode.html) ����������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/samples/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0017017�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/samples/docsample.cpp����������������������������������������������������0000664�0000000�0000000�00000004113�14411236400�0021471�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "../source/utf8.h" #include <iostream> #include <fstream> #include <string> #include <vector> using namespace std; int main(int argc, char** argv) { if (argc != 2) { cout << "\nUsage: docsample filename\n"; return 0; } const char* test_file_path = argv[1]; // Open the test file (must be UTF-8 encoded) ifstream fs8(test_file_path); if (!fs8.is_open()) { cout << "Could not open " << test_file_path << endl; return 0; } unsigned line_count = 1; string line; // Play with all the lines in the file while (getline(fs8, line)) { // check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function) #if __cplusplus >= 201103L // C++ 11 or later auto end_it = utf8::find_invalid(line.begin(), line.end()); #else string::iterator end_it = utf8::find_invalid(line.begin(), line.end()); #endif // C++ 11 if (end_it != line.end()) { cout << "Invalid UTF-8 encoding detected at line " << line_count << "\n"; cout << "This part is fine: " << string(line.begin(), end_it) << "\n"; } // Get the line length (at least for the valid part) ptrdiff_t length = utf8::distance(line.begin(), end_it); cout << "Length of line " << line_count << " is " << length << "\n"; // Convert it to utf-16 #if __cplusplus >= 201103L // C++ 11 or later u16string utf16line = utf8::utf8to16(line); #else vector<unsigned short> utf16line; utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line)); #endif // C++ 11 // And back to utf-8; #if __cplusplus >= 201103L // C++ 11 or later string utf8line = utf8::utf16to8(utf16line); #else string utf8line; utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line)); #endif // C++ 11 // Confirm that the conversion went OK: if (utf8line != string(line.begin(), end_it)) cout << "Error in UTF-16 conversion at line: " << line_count << "\n"; line_count++; } return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/source/������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0016653�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/source/utf8.h������������������������������������������������������������0000664�0000000�0000000�00000002761�14411236400�0017720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "utf8/checked.h" #include "utf8/unchecked.h" #endif // header guard ���������������ledger-3.3.2/lib/utfcpp/v3/source/utf8/�������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0017541�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/source/utf8/checked.h����������������������������������������������������0000664�0000000�0000000�00000026454�14411236400�0021313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2006-2016 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "core.h" #include <stdexcept> namespace utf8 { // Base for the exceptions that may be thrown from the library class exception : public ::std::exception { }; // Exceptions that may be thrown from the library functions. class invalid_code_point : public exception { uint32_t cp; public: invalid_code_point(uint32_t codepoint) : cp(codepoint) {} virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE { return "Invalid code point"; } uint32_t code_point() const {return cp;} }; class invalid_utf8 : public exception { uint8_t u8; public: invalid_utf8 (uint8_t u) : u8(u) {} invalid_utf8 (char c) : u8(static_cast<uint8_t>(c)) {} virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE { return "Invalid UTF-8"; } uint8_t utf8_octet() const {return u8;} }; class invalid_utf16 : public exception { uint16_t u16; public: invalid_utf16 (uint16_t u) : u16(u) {} virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE { return "Invalid UTF-16"; } uint16_t utf16_word() const {return u16;} }; class not_enough_room : public exception { public: virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE { return "Not enough space"; } }; /// The library API - functions intended to be called by the users template <typename octet_iterator> octet_iterator append(uint32_t cp, octet_iterator result) { if (!utf8::internal::is_code_point_valid(cp)) throw invalid_code_point(cp); return internal::append(cp, result); } template <typename octet_iterator, typename output_iterator> output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) { while (start != end) { octet_iterator sequence_start = start; internal::utf_error err_code = utf8::internal::validate_next(start, end); switch (err_code) { case internal::UTF8_OK : for (octet_iterator it = sequence_start; it != start; ++it) *out++ = *it; break; case internal::NOT_ENOUGH_ROOM: out = utf8::append (replacement, out); start = end; break; case internal::INVALID_LEAD: out = utf8::append (replacement, out); ++start; break; case internal::INCOMPLETE_SEQUENCE: case internal::OVERLONG_SEQUENCE: case internal::INVALID_CODE_POINT: out = utf8::append (replacement, out); ++start; // just one replacement mark for the sequence while (start != end && utf8::internal::is_trail(*start)) ++start; break; } } return out; } template <typename octet_iterator, typename output_iterator> inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) { static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); return utf8::replace_invalid(start, end, out, replacement_marker); } template <typename octet_iterator> uint32_t next(octet_iterator& it, octet_iterator end) { uint32_t cp = 0; internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); switch (err_code) { case internal::UTF8_OK : break; case internal::NOT_ENOUGH_ROOM : throw not_enough_room(); case internal::INVALID_LEAD : case internal::INCOMPLETE_SEQUENCE : case internal::OVERLONG_SEQUENCE : throw invalid_utf8(static_cast<uint8_t>(*it)); case internal::INVALID_CODE_POINT : throw invalid_code_point(cp); } return cp; } template <typename octet_iterator> uint32_t peek_next(octet_iterator it, octet_iterator end) { return utf8::next(it, end); } template <typename octet_iterator> uint32_t prior(octet_iterator& it, octet_iterator start) { // can't do much if it == start if (it == start) throw not_enough_room(); octet_iterator end = it; // Go back until we hit either a lead octet or start while (utf8::internal::is_trail(*(--it))) if (it == start) throw invalid_utf8(*it); // error - no lead byte in the sequence return utf8::peek_next(it, end); } template <typename octet_iterator, typename distance_type> void advance (octet_iterator& it, distance_type n, octet_iterator end) { const distance_type zero(0); if (n < zero) { // backward for (distance_type i = n; i < zero; ++i) utf8::prior(it, end); } else { // forward for (distance_type i = zero; i < n; ++i) utf8::next(it, end); } } template <typename octet_iterator> typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last) { typename std::iterator_traits<octet_iterator>::difference_type dist; for (dist = 0; first < last; ++dist) utf8::next(first, last); return dist; } template <typename u16bit_iterator, typename octet_iterator> octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first if (utf8::internal::is_lead_surrogate(cp)) { if (start != end) { uint32_t trail_surrogate = utf8::internal::mask16(*start++); if (utf8::internal::is_trail_surrogate(trail_surrogate)) cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; else throw invalid_utf16(static_cast<uint16_t>(trail_surrogate)); } else throw invalid_utf16(static_cast<uint16_t>(cp)); } // Lone trail surrogate else if (utf8::internal::is_trail_surrogate(cp)) throw invalid_utf16(static_cast<uint16_t>(cp)); result = utf8::append(cp, result); } return result; } template <typename u16bit_iterator, typename octet_iterator> u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start < end) { uint32_t cp = utf8::next(start, end); if (cp > 0xffff) { //make a surrogate pair *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else *result++ = static_cast<uint16_t>(cp); } return result; } template <typename octet_iterator, typename u32bit_iterator> octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::append(*(start++), result); return result; } template <typename octet_iterator, typename u32bit_iterator> u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start < end) (*result++) = utf8::next(start, end); return result; } // The iterator class template <typename octet_iterator> class iterator { octet_iterator it; octet_iterator range_start; octet_iterator range_end; public: typedef uint32_t value_type; typedef uint32_t* pointer; typedef uint32_t& reference; typedef std::ptrdiff_t difference_type; typedef std::bidirectional_iterator_tag iterator_category; iterator () {} explicit iterator (const octet_iterator& octet_it, const octet_iterator& rangestart, const octet_iterator& rangeend) : it(octet_it), range_start(rangestart), range_end(rangeend) { if (it < range_start || it > range_end) throw std::out_of_range("Invalid utf-8 iterator position"); } // the default "big three" are OK octet_iterator base () const { return it; } uint32_t operator * () const { octet_iterator temp = it; return utf8::next(temp, range_end); } bool operator == (const iterator& rhs) const { if (range_start != rhs.range_start || range_end != rhs.range_end) throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); } iterator& operator ++ () { utf8::next(it, range_end); return *this; } iterator operator ++ (int) { iterator temp = *this; utf8::next(it, range_end); return temp; } iterator& operator -- () { utf8::prior(it, range_start); return *this; } iterator operator -- (int) { iterator temp = *this; utf8::prior(it, range_start); return temp; } }; // class iterator } // namespace utf8 #if UTF_CPP_CPLUSPLUS >= 201703L // C++ 17 or later #include "cpp17.h" #elif UTF_CPP_CPLUSPLUS >= 201103L // C++ 11 or later #include "cpp11.h" #endif // C++ 11 or later #endif //header guard ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/source/utf8/core.h�������������������������������������������������������0000664�0000000�0000000�00000032105�14411236400�0020643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include <iterator> // Determine the C++ standard version. // If the user defines UTF_CPP_CPLUSPLUS, use that. // Otherwise, trust the unreliable predefined macro __cplusplus #if !defined UTF_CPP_CPLUSPLUS #define UTF_CPP_CPLUSPLUS __cplusplus #endif #if UTF_CPP_CPLUSPLUS >= 201103L // C++ 11 or later #define UTF_CPP_OVERRIDE override #define UTF_CPP_NOEXCEPT noexcept #else // C++ 98/03 #define UTF_CPP_OVERRIDE #define UTF_CPP_NOEXCEPT throw() #endif // C++ 11 or later namespace utf8 { // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers // You may need to change them to match your system. // These typedefs have the same names as ones from cstdint, or boost/cstdint typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; // Helper code - not intended to be directly called by the library users. May be changed at any time namespace internal { // Unicode constants // Leading (high) surrogates: 0xd800 - 0xdbff // Trailing (low) surrogates: 0xdc00 - 0xdfff const uint16_t LEAD_SURROGATE_MIN = 0xd800u; const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; const uint16_t LEAD_OFFSET = 0xd7c0u; // LEAD_SURROGATE_MIN - (0x10000 >> 10) const uint32_t SURROGATE_OFFSET = 0xfca02400u; // 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN // Maximum valid value for a Unicode code point const uint32_t CODE_POINT_MAX = 0x0010ffffu; template<typename octet_type> inline uint8_t mask8(octet_type oc) { return static_cast<uint8_t>(0xff & oc); } template<typename u16_type> inline uint16_t mask16(u16_type oc) { return static_cast<uint16_t>(0xffff & oc); } template<typename octet_type> inline bool is_trail(octet_type oc) { return ((utf8::internal::mask8(oc) >> 6) == 0x2); } template <typename u16> inline bool is_lead_surrogate(u16 cp) { return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); } template <typename u16> inline bool is_trail_surrogate(u16 cp) { return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); } template <typename u16> inline bool is_surrogate(u16 cp) { return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); } template <typename u32> inline bool is_code_point_valid(u32 cp) { return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); } template <typename octet_iterator> inline typename std::iterator_traits<octet_iterator>::difference_type sequence_length(octet_iterator lead_it) { uint8_t lead = utf8::internal::mask8(*lead_it); if (lead < 0x80) return 1; else if ((lead >> 5) == 0x6) return 2; else if ((lead >> 4) == 0xe) return 3; else if ((lead >> 3) == 0x1e) return 4; else return 0; } template <typename octet_difference_type> inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) { if (cp < 0x80) { if (length != 1) return true; } else if (cp < 0x800) { if (length != 2) return true; } else if (cp < 0x10000) { if (length != 3) return true; } return false; } enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; /// Helper for get_sequence_x template <typename octet_iterator> utf_error increase_safely(octet_iterator& it, octet_iterator end) { if (++it == end) return NOT_ENOUGH_ROOM; if (!utf8::internal::is_trail(*it)) return INCOMPLETE_SEQUENCE; return UTF8_OK; } #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} /// get_sequence_x functions decode utf-8 sequences of the length x template <typename octet_iterator> utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); return UTF8_OK; } template <typename octet_iterator> utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); return UTF8_OK; } template <typename octet_iterator> utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (*it) & 0x3f; return UTF8_OK; } template <typename octet_iterator> utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (*it) & 0x3f; return UTF8_OK; } #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR template <typename octet_iterator> utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; // Save the original value of it so we can go back in case of failure // Of course, it does not make much sense with i.e. stream iterators octet_iterator original_it = it; uint32_t cp = 0; // Determine the sequence length based on the lead octet typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type; const octet_difference_type length = utf8::internal::sequence_length(it); // Get trail octets and calculate the code point utf_error err = UTF8_OK; switch (length) { case 0: return INVALID_LEAD; case 1: err = utf8::internal::get_sequence_1(it, end, cp); break; case 2: err = utf8::internal::get_sequence_2(it, end, cp); break; case 3: err = utf8::internal::get_sequence_3(it, end, cp); break; case 4: err = utf8::internal::get_sequence_4(it, end, cp); break; } if (err == UTF8_OK) { // Decoding succeeded. Now, security checks... if (utf8::internal::is_code_point_valid(cp)) { if (!utf8::internal::is_overlong_sequence(cp, length)){ // Passed! Return here. code_point = cp; ++it; return UTF8_OK; } else err = OVERLONG_SEQUENCE; } else err = INVALID_CODE_POINT; } // Failure branch - restore the original value of the iterator it = original_it; return err; } template <typename octet_iterator> inline utf_error validate_next(octet_iterator& it, octet_iterator end) { uint32_t ignored; return utf8::internal::validate_next(it, end, ignored); } // Internal implementation of both checked and unchecked append() function // This function will be invoked by the overloads below, as they will know // the octet_type. template <typename octet_iterator, typename octet_type> octet_iterator append(uint32_t cp, octet_iterator result) { if (cp < 0x80) // one octet *(result++) = static_cast<octet_type>(cp); else if (cp < 0x800) { // two octets *(result++) = static_cast<octet_type>((cp >> 6) | 0xc0); *(result++) = static_cast<octet_type>((cp & 0x3f) | 0x80); } else if (cp < 0x10000) { // three octets *(result++) = static_cast<octet_type>((cp >> 12) | 0xe0); *(result++) = static_cast<octet_type>(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast<octet_type>((cp & 0x3f) | 0x80); } else { // four octets *(result++) = static_cast<octet_type>((cp >> 18) | 0xf0); *(result++) = static_cast<octet_type>(((cp >> 12) & 0x3f)| 0x80); *(result++) = static_cast<octet_type>(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast<octet_type>((cp & 0x3f) | 0x80); } return result; } // One of the following overloads will be invoked from the API calls // A simple (but dangerous) case: the caller appends byte(s) to a char array inline char* append(uint32_t cp, char* result) { return append<char*, char>(cp, result); } // Hopefully, most common case: the caller uses back_inserter // i.e. append(cp, std::back_inserter(str)); template<typename container_type> std::back_insert_iterator<container_type> append (uint32_t cp, std::back_insert_iterator<container_type> result) { return append<std::back_insert_iterator<container_type>, typename container_type::value_type>(cp, result); } // The caller uses some other kind of output operator - not covered above // Note that in this case we are not able to determine octet_type // so we assume it's uint_8; that can cause a conversion warning if we are wrong. template <typename octet_iterator> octet_iterator append(uint32_t cp, octet_iterator result) { return append<octet_iterator, uint8_t>(cp, result); } } // namespace internal /// The library API - functions intended to be called by the users // Byte order mark const uint8_t bom[] = {0xef, 0xbb, 0xbf}; template <typename octet_iterator> octet_iterator find_invalid(octet_iterator start, octet_iterator end) { octet_iterator result = start; while (result != end) { utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); if (err_code != internal::UTF8_OK) return result; } return result; } template <typename octet_iterator> inline bool is_valid(octet_iterator start, octet_iterator end) { return (utf8::find_invalid(start, end) == end); } template <typename octet_iterator> inline bool starts_with_bom (octet_iterator it, octet_iterator end) { return ( ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) ); } } // namespace utf8 #endif // header guard �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/source/utf8/cpp11.h������������������������������������������������������0000664�0000000�0000000�00000006402�14411236400�0020640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2018 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_a184c22c_d012_11e8_a8d5_f2801f1b9fd1 #define UTF8_FOR_CPP_a184c22c_d012_11e8_a8d5_f2801f1b9fd1 #include "checked.h" #include <string> namespace utf8 { inline void append(char32_t cp, std::string& s) { append(uint32_t(cp), std::back_inserter(s)); } inline std::string utf16to8(const std::u16string& s) { std::string result; utf16to8(s.begin(), s.end(), std::back_inserter(result)); return result; } inline std::u16string utf8to16(const std::string& s) { std::u16string result; utf8to16(s.begin(), s.end(), std::back_inserter(result)); return result; } inline std::string utf32to8(const std::u32string& s) { std::string result; utf32to8(s.begin(), s.end(), std::back_inserter(result)); return result; } inline std::u32string utf8to32(const std::string& s) { std::u32string result; utf8to32(s.begin(), s.end(), std::back_inserter(result)); return result; } inline std::size_t find_invalid(const std::string& s) { std::string::const_iterator invalid = find_invalid(s.begin(), s.end()); return (invalid == s.end()) ? std::string::npos : static_cast<std::size_t>(invalid - s.begin()); } inline bool is_valid(const std::string& s) { return is_valid(s.begin(), s.end()); } inline std::string replace_invalid(const std::string& s, char32_t replacement) { std::string result; replace_invalid(s.begin(), s.end(), std::back_inserter(result), replacement); return result; } inline std::string replace_invalid(const std::string& s) { std::string result; replace_invalid(s.begin(), s.end(), std::back_inserter(result)); return result; } inline bool starts_with_bom(const std::string& s) { return starts_with_bom(s.begin(), s.end()); } } // namespace utf8 #endif // header guard ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/source/utf8/cpp17.h������������������������������������������������������0000664�0000000�0000000�00000006372�14411236400�0020654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2018 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_7e906c01_03a3_4daf_b420_ea7ea952b3c9 #define UTF8_FOR_CPP_7e906c01_03a3_4daf_b420_ea7ea952b3c9 #include "checked.h" #include <string> namespace utf8 { inline void append(char32_t cp, std::string& s) { append(uint32_t(cp), std::back_inserter(s)); } inline std::string utf16to8(std::u16string_view s) { std::string result; utf16to8(s.begin(), s.end(), std::back_inserter(result)); return result; } inline std::u16string utf8to16(std::string_view s) { std::u16string result; utf8to16(s.begin(), s.end(), std::back_inserter(result)); return result; } inline std::string utf32to8(std::u32string_view s) { std::string result; utf32to8(s.begin(), s.end(), std::back_inserter(result)); return result; } inline std::u32string utf8to32(std::string_view s) { std::u32string result; utf8to32(s.begin(), s.end(), std::back_inserter(result)); return result; } inline std::size_t find_invalid(std::string_view s) { std::string_view::const_iterator invalid = find_invalid(s.begin(), s.end()); return (invalid == s.end()) ? std::string_view::npos : static_cast<std::size_t>(invalid - s.begin()); } inline bool is_valid(std::string_view s) { return is_valid(s.begin(), s.end()); } inline std::string replace_invalid(std::string_view s, char32_t replacement) { std::string result; replace_invalid(s.begin(), s.end(), std::back_inserter(result), replacement); return result; } inline std::string replace_invalid(std::string_view s) { std::string result; replace_invalid(s.begin(), s.end(), std::back_inserter(result)); return result; } inline bool starts_with_bom(std::string_view s) { return starts_with_bom(s.begin(), s.end()); } } // namespace utf8 #endif // header guard ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/source/utf8/unchecked.h��������������������������������������������������0000664�0000000�0000000�00000023330�14411236400�0021644�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "core.h" namespace utf8 { namespace unchecked { template <typename octet_iterator> octet_iterator append(uint32_t cp, octet_iterator result) { return internal::append(cp, result); } template <typename octet_iterator, typename output_iterator> output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) { while (start != end) { octet_iterator sequence_start = start; internal::utf_error err_code = utf8::internal::validate_next(start, end); switch (err_code) { case internal::UTF8_OK : for (octet_iterator it = sequence_start; it != start; ++it) *out++ = *it; break; case internal::NOT_ENOUGH_ROOM: out = utf8::unchecked::append (replacement, out); start = end; break; case internal::INVALID_LEAD: out = utf8::unchecked::append (replacement, out); ++start; break; case internal::INCOMPLETE_SEQUENCE: case internal::OVERLONG_SEQUENCE: case internal::INVALID_CODE_POINT: out = utf8::unchecked::append (replacement, out); ++start; // just one replacement mark for the sequence while (start != end && utf8::internal::is_trail(*start)) ++start; break; } } return out; } template <typename octet_iterator, typename output_iterator> inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) { static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); return utf8::unchecked::replace_invalid(start, end, out, replacement_marker); } template <typename octet_iterator> uint32_t next(octet_iterator& it) { uint32_t cp = utf8::internal::mask8(*it); typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it); switch (length) { case 1: break; case 2: it++; cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); break; case 3: ++it; cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); ++it; cp += (*it) & 0x3f; break; case 4: ++it; cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); ++it; cp += (utf8::internal::mask8(*it) << 6) & 0xfff; ++it; cp += (*it) & 0x3f; break; } ++it; return cp; } template <typename octet_iterator> uint32_t peek_next(octet_iterator it) { return utf8::unchecked::next(it); } template <typename octet_iterator> uint32_t prior(octet_iterator& it) { while (utf8::internal::is_trail(*(--it))) ; octet_iterator temp = it; return utf8::unchecked::next(temp); } template <typename octet_iterator, typename distance_type> void advance (octet_iterator& it, distance_type n) { const distance_type zero(0); if (n < zero) { // backward for (distance_type i = n; i < zero; ++i) utf8::unchecked::prior(it); } else { // forward for (distance_type i = zero; i < n; ++i) utf8::unchecked::next(it); } } template <typename octet_iterator> typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last) { typename std::iterator_traits<octet_iterator>::difference_type dist; for (dist = 0; first < last; ++dist) utf8::unchecked::next(first); return dist; } template <typename u16bit_iterator, typename octet_iterator> octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first if (utf8::internal::is_lead_surrogate(cp)) { uint32_t trail_surrogate = utf8::internal::mask16(*start++); cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; } result = utf8::unchecked::append(cp, result); } return result; } template <typename u16bit_iterator, typename octet_iterator> u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start < end) { uint32_t cp = utf8::unchecked::next(start); if (cp > 0xffff) { //make a surrogate pair *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else *result++ = static_cast<uint16_t>(cp); } return result; } template <typename octet_iterator, typename u32bit_iterator> octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::unchecked::append(*(start++), result); return result; } template <typename octet_iterator, typename u32bit_iterator> u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start < end) (*result++) = utf8::unchecked::next(start); return result; } // The iterator class template <typename octet_iterator> class iterator { octet_iterator it; public: typedef uint32_t value_type; typedef uint32_t* pointer; typedef uint32_t& reference; typedef std::ptrdiff_t difference_type; typedef std::bidirectional_iterator_tag iterator_category; iterator () {} explicit iterator (const octet_iterator& octet_it): it(octet_it) {} // the default "big three" are OK octet_iterator base () const { return it; } uint32_t operator * () const { octet_iterator temp = it; return utf8::unchecked::next(temp); } bool operator == (const iterator& rhs) const { return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); } iterator& operator ++ () { ::std::advance(it, utf8::internal::sequence_length(it)); return *this; } iterator operator ++ (int) { iterator temp = *this; ::std::advance(it, utf8::internal::sequence_length(it)); return temp; } iterator& operator -- () { utf8::unchecked::prior(it); return *this; } iterator operator -- (int) { iterator temp = *this; utf8::unchecked::prior(it); return temp; } }; // class iterator } // namespace utf8::unchecked } // namespace utf8 #endif // header guard ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0016515�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/CMakeLists.txt�����������������������������������������������������0000664�0000000�0000000�00000003150�14411236400�0021254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������add_executable(negative ${PROJECT_SOURCE_DIR}/tests/negative.cpp) add_executable(cpp11 ${PROJECT_SOURCE_DIR}/tests/test_cpp11.cpp) add_executable(cpp17 ${PROJECT_SOURCE_DIR}/tests/test_cpp17.cpp) add_executable(apitests ${PROJECT_SOURCE_DIR}/tests/apitests.cpp) add_executable(noexceptionstests ${PROJECT_SOURCE_DIR}/tests/noexceptionstests.cpp) target_link_libraries(negative PRIVATE utf8::cpp) target_link_libraries(cpp11 PRIVATE utf8::cpp) target_link_libraries(cpp17 PRIVATE utf8::cpp) target_link_libraries(apitests PRIVATE utf8::cpp) target_link_libraries(noexceptionstests PRIVATE utf8::cpp) target_compile_options(${PROJECT_NAME} INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/W4> $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Wconversion>) target_compile_options(noexceptionstests PUBLIC -fno-exceptions) set_target_properties(negative apitests noexceptionstests PROPERTIES CXX_STANDARD 98 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) set_target_properties(cpp11 PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) set_target_properties(cpp17 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) add_test(negative_test negative ${PROJECT_SOURCE_DIR}/tests/test_data/utf8_invalid.txt) add_test(cpp11_test cpp11) add_test(cpp17_test cpp17) add_test(api_test apitests) add_test(noexceptions_test noexceptionstests) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/apitests.cpp�������������������������������������������������������0000664�0000000�0000000�00000000252�14411236400�0021054�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "../extern/ftest/ftest.h" #include "test_checked_api.h" #include "test_checked_iterator.h" #include "test_unchecked_api.h" #include "test_unchecked_iterator.h" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/docker/������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0017764�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/docker/Dockerfile��������������������������������������������������0000664�0000000�0000000�00000000175�14411236400�0021761�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FROM debian:buster-slim RUN apt-get update \ && apt-get install -y make g++ cmake git \ && rm -rf /var/lib/apt/lists/* ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/negative.cpp�������������������������������������������������������0000664�0000000�0000000�00000004137�14411236400�0021030�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "utf8.h" using namespace utf8; #include <string> #include <iostream> #include <fstream> #include <algorithm> using namespace std; const unsigned INVALID_LINES[] = { 75, 76, 83, 84, 85, 93, 102, 103, 105, 106, 107, 108, 109, 110, 114, 115, 116, 117, 124, 125, 130, 135, 140, 145, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 169, 175, 176, 177, 207, 208, 209, 210, 211, 220, 221, 222, 223, 224, 232, 233, 234, 235, 236, 247, 248, 249, 250, 251, 252, 253, 257, 258, 259, 260, 261, 262, 263, 264}; const unsigned* INVALID_LINES_END = INVALID_LINES + sizeof(INVALID_LINES)/sizeof(unsigned); int main(int argc, char** argv) { string test_file_path; if (argc == 2) test_file_path = argv[1]; else { cout << "Wrong number of arguments" << endl; return 1; } // Open the test file ifstream fs8(test_file_path.c_str()); if (!fs8.is_open()) { cout << "Could not open " << test_file_path << endl; return 1; } // Read it line by line unsigned int line_count = 0; char byte; while (!fs8.eof()) { string line; while ((byte = static_cast<char>(fs8.get())) != '\n' && !fs8.eof()) line.push_back(byte); line_count++; bool expected_valid = (find(INVALID_LINES, INVALID_LINES_END, line_count) == INVALID_LINES_END); // Print out lines that contain unexpected invalid UTF-8 if (!is_valid(line.begin(), line.end())) { if (expected_valid) { cout << "Unexpected invalid utf-8 at line " << line_count << '\n'; return 1; } // try fixing it: string fixed_line; replace_invalid(line.begin(), line.end(), back_inserter(fixed_line)); if (!is_valid(fixed_line.begin(), fixed_line.end())) { cout << "replace_invalid() resulted in an invalid utf-8 at line " << line_count << '\n'; return 1; } } else if (!expected_valid) { cout << "Invalid utf-8 NOT detected at line " << line_count << '\n'; return 1; } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/noexceptionstests.cpp����������������������������������������������0000664�0000000�0000000�00000000151�14411236400�0023017�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "../extern/ftest/ftest.h" #include "test_unchecked_api.h" #include "test_unchecked_iterator.h" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/test_checked_api.h�������������������������������������������������0000664�0000000�0000000�00000013367�14411236400�0022156�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef UTF8_FOR_CPP_TEST_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_TEST_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "utf8.h" #include <string> #include <vector> using namespace utf8; using namespace std; TEST(CheckedAPITests, test_append) { unsigned char u[5] = {0,0,0,0,0}; append(0x0448, u); EXPECT_EQ (u[0], 0xd1); EXPECT_EQ (u[1], 0x88); EXPECT_EQ (u[2], 0); EXPECT_EQ (u[3], 0); EXPECT_EQ (u[4], 0); append(0x65e5, u); EXPECT_EQ (u[0], 0xe6); EXPECT_EQ (u[1], 0x97); EXPECT_EQ (u[2], 0xa5); EXPECT_EQ (u[3], 0); EXPECT_EQ (u[4], 0); append(0x3044, u); EXPECT_EQ (u[0], 0xe3); EXPECT_EQ (u[1], 0x81); EXPECT_EQ (u[2], 0x84); EXPECT_EQ (u[3], 0); EXPECT_EQ (u[4], 0); append(0x10346, u); EXPECT_EQ (u[0], 0xf0); EXPECT_EQ (u[1], 0x90); EXPECT_EQ (u[2], 0x8d); EXPECT_EQ (u[3], 0x86); EXPECT_EQ (u[4], 0); // Ensure no warnings with plain char char c[2] = {0,0}; append('a', c); EXPECT_EQ (c[0], 'a'); EXPECT_EQ (c[1], 0); } TEST(CheckedAPITests, test_next) { const char* twochars = "\xe6\x97\xa5\xd1\x88"; const char* w = twochars; unsigned int cp = next(w, twochars + 6); EXPECT_EQ (cp, 0x65e5); EXPECT_EQ (w, twochars + 3); const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; w = threechars; cp = next(w, threechars + 9); EXPECT_EQ (cp, 0x10346); EXPECT_EQ (w, threechars + 4); cp = next(w, threechars + 9); EXPECT_EQ (cp, 0x65e5); EXPECT_EQ (w, threechars + 7); cp = next(w, threechars + 9); EXPECT_EQ (cp, 0x0448); EXPECT_EQ (w, threechars + 9); } TEST(CheckedAPITests, test_peek_next) { const char* const cw = "\xe6\x97\xa5\xd1\x88"; unsigned int cp = peek_next(cw, cw + 6); EXPECT_EQ (cp, 0x65e5); } TEST(CheckedAPITests, test_prior) { const char* twochars = "\xe6\x97\xa5\xd1\x88"; const char* w = twochars + 3; unsigned int cp = prior (w, twochars); EXPECT_EQ (cp, 0x65e5); EXPECT_EQ (w, twochars); const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; w = threechars + 9; cp = prior(w, threechars); EXPECT_EQ (cp, 0x0448); EXPECT_EQ (w, threechars + 7); cp = prior(w, threechars); EXPECT_EQ (cp, 0x65e5); EXPECT_EQ (w, threechars + 4); cp = prior(w, threechars); EXPECT_EQ (cp, 0x10346); EXPECT_EQ (w, threechars); } TEST(CheckedAPITests, test_advance) { const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; const char* w = threechars; advance(w, 2, threechars + 9); EXPECT_EQ(w, threechars + 7); advance(w, -2, threechars); EXPECT_EQ(w, threechars); advance(w, 3, threechars + 9); EXPECT_EQ(w, threechars + 9); advance(w, -2, threechars); EXPECT_EQ(w, threechars + 4); advance(w, -1, threechars); EXPECT_EQ(w, threechars); } TEST(CheckedAPITests, test_distance) { const char* twochars = "\xe6\x97\xa5\xd1\x88"; size_t dist = static_cast<size_t>(utf8::distance(twochars, twochars + 5)); EXPECT_EQ (dist, 2); } TEST(CheckedAPITests, test_utf32to8) { unsigned int utf32string[] = {0x448, 0x65E5, 0x10346, 0}; string utf8result; utf32to8(utf32string, utf32string + 3, back_inserter(utf8result)); EXPECT_EQ (utf8result.size(), 9); } TEST(CheckedAPITests, test_utf8to32) { const char* twochars = "\xe6\x97\xa5\xd1\x88"; vector<unsigned int> utf32result; utf8to32(twochars, twochars + 5, back_inserter(utf32result)); EXPECT_EQ (utf32result.size(), 2); } TEST(CheckedAPITests, test_utf16to8) { unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; string utf8result; utf16to8(utf16string, utf16string + 5, back_inserter(utf8result)); EXPECT_EQ (utf8result.size(), 10); } TEST(CheckedAPITests, test_utf8to16) { char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; vector <unsigned short> utf16result; utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result)); EXPECT_EQ (utf16result.size(), 4); EXPECT_EQ (utf16result[2], 0xd834); EXPECT_EQ (utf16result[3], 0xdd1e); } TEST(CheckedAPITests, test_replace_invalid) { char invalid_sequence[] = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; vector<char> replace_invalid_result; replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), std::back_inserter(replace_invalid_result), '?'); bool bvalid = is_valid(replace_invalid_result.begin(), replace_invalid_result.end()); EXPECT_TRUE (bvalid); const char fixed_invalid_sequence[] = "a????z"; EXPECT_EQ (sizeof(fixed_invalid_sequence), replace_invalid_result.size()); EXPECT_TRUE (std::equal(replace_invalid_result.begin(), replace_invalid_result.begin() + sizeof(fixed_invalid_sequence), fixed_invalid_sequence)); } TEST(CheckedAPITests, test_find_invalid) { char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa"; char* invalid = find_invalid(utf_invalid, utf_invalid + 6); EXPECT_EQ (invalid, utf_invalid + 5); } TEST(CheckedAPITests, test_is_valid) { char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa"; bool bvalid = is_valid(utf_invalid, utf_invalid + 6); EXPECT_FALSE (bvalid); char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; bvalid = is_valid(utf8_with_surrogates, utf8_with_surrogates + 9); EXPECT_TRUE (bvalid); } TEST(CheckedAPITests, test_starts_with_bom) { unsigned char byte_order_mark[] = {0xef, 0xbb, 0xbf}; bool bbom = starts_with_bom(byte_order_mark, byte_order_mark + sizeof(byte_order_mark)); EXPECT_TRUE (bbom); const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; bool no_bbom = starts_with_bom(threechars, threechars + sizeof(threechars)); EXPECT_FALSE (no_bbom); } #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/test_checked_iterator.h��������������������������������������������0000664�0000000�0000000�00000002173�14411236400�0023227�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef UTF8_FOR_CPP_TEST_CHECKED_ITERATOR_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_TEST_CHECKED_ITERATOR_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "utf8.h" using namespace utf8; TEST(CheckedIteratrTests, test_increment) { const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; utf8::iterator<const char*> it(threechars, threechars, threechars + 9); utf8::iterator<const char*> it2 = it; EXPECT_EQ (it2, it); EXPECT_EQ (*it, 0x10346); EXPECT_EQ (*(++it), 0x65e5); EXPECT_EQ ((*it++), 0x65e5); EXPECT_EQ (*it, 0x0448); EXPECT_NE (it, it2); utf8::iterator<const char*> endit (threechars + 9, threechars, threechars + 9); EXPECT_EQ (++it, endit); } TEST(CheckedIteratrTests, test_decrement) { const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; utf8::iterator<const char*> it(threechars+9, threechars, threechars + 9); EXPECT_EQ (*(--it), 0x0448); EXPECT_EQ ((*it--), 0x0448); EXPECT_EQ (*it, 0x65e5); EXPECT_EQ (--it, utf8::iterator<const char*>(threechars, threechars, threechars + 9)); EXPECT_EQ (*it, 0x10346); } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/test_cpp11.cpp�����������������������������������������������������0000664�0000000�0000000�00000005734�14411236400�0021215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "../extern/ftest/ftest.h" #include "utf8.h" #include <string> using namespace utf8; using namespace std; #if __cplusplus >= 201103L // C++ 11 or later TEST(CPP11APITests, test_append) { string u; append(0x0448, u); EXPECT_EQ (u[0], char(0xd1)); EXPECT_EQ (u[1], char(0x88)); EXPECT_EQ (u.length(), 2); u.clear(); append(0x65e5, u); EXPECT_EQ (u[0], char(0xe6)); EXPECT_EQ (u[1], char(0x97)); EXPECT_EQ (u[2], char(0xa5)); EXPECT_EQ (u.length(), 3); u.clear(); append(0x3044, u); EXPECT_EQ (u[0], char(0xe3)); EXPECT_EQ (u[1], char(0x81)); EXPECT_EQ (u[2], char(0x84)); EXPECT_EQ (u.length(), 3); u.clear(); append(0x10346, u); EXPECT_EQ (u[0], char(0xf0)); EXPECT_EQ (u[1], char(0x90)); EXPECT_EQ (u[2], char(0x8d)); EXPECT_EQ (u[3], char(0x86)); EXPECT_EQ (u.length(), 4); } TEST(CPP11APITests, test_utf16to8) { u16string utf16string = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; string u = utf16to8(utf16string); EXPECT_EQ (u.size(), 10); } TEST(CPP11APITests, test_utf8to16) { string utf8_with_surrogates = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; u16string utf16result = utf8to16(utf8_with_surrogates); EXPECT_EQ (utf16result.size(), 4); EXPECT_EQ (utf16result[2], 0xd834); EXPECT_EQ (utf16result[3], 0xdd1e); // Just to make sure it compiles with string literals utf8to16(u8"simple"); utf8to16("simple"); } TEST(CPP11APITests, test_utf32to8) { u32string utf32string = {0x448, 0x65E5, 0x10346}; string utf8result = utf32to8(utf32string); EXPECT_EQ (utf8result.size(), 9); } TEST(CPP11APITests, test_utf8to32) { const char* twochars = "\xe6\x97\xa5\xd1\x88"; u32string utf32result = utf8to32(twochars); EXPECT_EQ (utf32result.size(), 2); } TEST(CPP11APITests, test_find_invalid) { string utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; auto invalid = find_invalid(utf_invalid); EXPECT_EQ (invalid, 5); } TEST(CPP11APITests, test_is_valid) { string utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; bool bvalid = is_valid(utf_invalid); EXPECT_FALSE (bvalid); string utf8_with_surrogates = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; bvalid = is_valid(utf8_with_surrogates); EXPECT_TRUE (bvalid); } TEST(CPP11APITests, test_replace_invalid) { string invalid_sequence = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; string replace_invalid_result = replace_invalid(invalid_sequence, '?'); bool bvalid = is_valid(replace_invalid_result); EXPECT_TRUE (bvalid); const string fixed_invalid_sequence = "a????z"; EXPECT_EQ(fixed_invalid_sequence, replace_invalid_result); } TEST(CPP11APITests, test_starts_with_bom) { string byte_order_mark = {char(0xef), char(0xbb), char(0xbf)}; bool bbom = starts_with_bom(byte_order_mark); EXPECT_TRUE (bbom); string threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; bool no_bbom = starts_with_bom(threechars); EXPECT_FALSE (no_bbom); } #endif // C++ 11 or later ������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/test_cpp17.cpp�����������������������������������������������������0000664�0000000�0000000�00000005063�14411236400�0021216�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "../extern/ftest/ftest.h" #include "utf8.h" #include <string> using namespace utf8; using namespace std; #if __cplusplus >= 201703L // C++ 17 or later TEST(CPP17APITests, test_utf16to8) { u16string utf16string = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; u16string_view utf16stringview(u16string); string u = utf16to8(utf16string); EXPECT_EQ (u.size(), 10); } TEST(CPP17APITests, test_utf8to16) { string_view utf8_with_surrogates = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; u16string utf16result = utf8to16(utf8_with_surrogates); EXPECT_EQ (utf16result.size(), 4); EXPECT_EQ (utf16result[2], 0xd834); EXPECT_EQ (utf16result[3], 0xdd1e); } TEST(CPP17APITests, test_utf32to8) { u32string utf32string = {0x448, 0x65E5, 0x10346}; u32string_view utf32stringview(utf32string); string utf8result = utf32to8(utf32stringview); EXPECT_EQ (utf8result.size(), 9); } TEST(CPP17APITests, test_utf8to32) { string_view twochars = "\xe6\x97\xa5\xd1\x88"; u32string utf32result = utf8to32(twochars); EXPECT_EQ (utf32result.size(), 2); } TEST(CPP17APITests, test_find_invalid) { string_view utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; auto invalid = find_invalid(utf_invalid); EXPECT_EQ (invalid, 5); } TEST(CPP17APITests, test_is_valid) { string_view utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; bool bvalid = is_valid(utf_invalid); EXPECT_FALSE (bvalid); string_view utf8_with_surrogates = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; bvalid = is_valid(utf8_with_surrogates); EXPECT_TRUE (bvalid); } TEST(CPP17APITests, test_replace_invalid) { string_view invalid_sequence = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; string replace_invalid_result = replace_invalid(invalid_sequence, '?'); bool bvalid = is_valid(replace_invalid_result); EXPECT_TRUE (bvalid); const string fixed_invalid_sequence = "a????z"; EXPECT_EQ(fixed_invalid_sequence, replace_invalid_result); } TEST(CPP17APITests, test_starts_with_bom) { string byte_order_mark = {char(0xef), char(0xbb), char(0xbf)}; string_view byte_order_mark_view(byte_order_mark); bool bbom = starts_with_bom(byte_order_mark_view); EXPECT_TRUE (bbom); string_view threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; bool no_bbom = starts_with_bom(threechars); EXPECT_FALSE (no_bbom); } TEST(CPP17APITests, string_class_and_literals) { const char* twochars = u8"ab"; EXPECT_TRUE (is_valid(twochars)); const string two_chars_string(twochars); EXPECT_TRUE (is_valid(two_chars_string)); } #endif // C++ 11 or later �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/test_data/���������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0020465�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/test_data/utf8_invalid.txt�����������������������������������������0000664�0000000�0000000�00000047052�14411236400�0023632�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������UTF-8 decoder capability and stress test ---------------------------------------- Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> - 2003-02-19 This test file can help you examine, how your UTF-8 decoder handles various types of correct, malformed, or otherwise interesting UTF-8 sequences. This file is not meant to be a conformance test. It does not prescribes any particular outcome and therefore there is no way to "pass" or "fail" this test file, even though the texts suggests a preferable decoder behaviour at some places. The aim is instead to help you think about and test the behaviour of your UTF-8 on a systematic collection of unusual inputs. Experience so far suggests that most first-time authors of UTF-8 decoders find at least one serious problem in their decoder by using this file. The test lines below cover boundary conditions, malformed UTF-8 sequences as well as correctly encoded UTF-8 sequences of Unicode code points that should never occur in a correct UTF-8 file. According to ISO 10646-1:2000, sections D.7 and 2.3c, a device receiving UTF-8 shall interpret a "malformed sequence in the same way that it interprets a character that is outside the adopted subset" and "characters that are not within the adopted subset shall be indicated to the user" by a receiving device. A quite commonly used approach in UTF-8 decoders is to replace any malformed UTF-8 sequence by a replacement character (U+FFFD), which looks a bit like an inverted question mark, or a similar symbol. It might be a good idea to visually distinguish a malformed UTF-8 sequence from a correctly encoded Unicode character that is just not available in the current font but otherwise fully legal, even though ISO 10646-1 doesn't mandate this. In any case, just ignoring malformed sequences or unavailable characters does not conform to ISO 10646, will make debugging more difficult, and can lead to user confusion. Please check, whether a malformed UTF-8 sequence is (1) represented at all, (2) represented by exactly one single replacement character (or equivalent signal), and (3) the following quotation mark after an illegal UTF-8 sequence is correctly displayed, i.e. proper resynchronization takes place immageately after any malformed sequence. This file says "THE END" in the last line, so if you don't see that, your decoder crashed somehow before, which should always be cause for concern. All lines in this file are exactly 79 characters long (plus the line feed). In addition, all lines end with "|", except for the two test lines 2.1.1 and 2.2.1, which contain non-printable ASCII controls U+0000 and U+007F. If you display this file with a fixed-width font, these "|" characters should all line up in column 79 (right margin). This allows you to test quickly, whether your UTF-8 decoder finds the correct number of characters in every line, that is whether each malformed sequences is replaced by a single replacement character. Note that as an alternative to the notion of malformed sequence used here, it is also a perfectly acceptable (and in some situations even preferable) solution to represent each individual byte of a malformed sequence by a replacement character. If you follow this strategy in your decoder, then please ignore the "|" column. Here come the tests: | | 1 Some correct UTF-8 text | | You should see the Greek word 'kosme': "κόσμε" | | 2 Boundary condition test cases | | 2.1 First possible sequence of a certain length | | 2.1.1 1 byte (U-00000000): "�" 2.1.2 2 bytes (U-00000080): "€" | 2.1.3 3 bytes (U-00000800): "ࠀ" | 2.1.4 4 bytes (U-00010000): "𐀀" | 2.1.5 5 bytes (U-00200000): "" | 2.1.6 6 bytes (U-04000000): "" | | 2.2 Last possible sequence of a certain length | | 2.2.1 1 byte (U-0000007F): "" 2.2.2 2 bytes (U-000007FF): "߿" | 2.2.3 3 bytes (U-0000FFFF): "￿" | 2.2.4 4 bytes (U-001FFFFF): "" | 2.2.5 5 bytes (U-03FFFFFF): "" | 2.2.6 6 bytes (U-7FFFFFFF): "" | | 2.3 Other boundary conditions | | 2.3.1 U-0000D7FF = ed 9f bf = "퟿" | 2.3.2 U-0000E000 = ee 80 80 = "" | 2.3.3 U-0000FFFD = ef bf bd = "�" | 2.3.4 U-0010FFFF = f4 8f bf bf = "􏿿" | 2.3.5 U-00110000 = f4 90 80 80 = "" | | 3 Malformed sequences | | 3.1 Unexpected continuation bytes | | Each unexpected continuation byte should be separately signalled as a | malformed sequence of its own. | | 3.1.1 First continuation byte 0x80: "" | 3.1.2 Last continuation byte 0xbf: "" | | 3.1.3 2 continuation bytes: "" | 3.1.4 3 continuation bytes: "" | 3.1.5 4 continuation bytes: "" | 3.1.6 5 continuation bytes: "" | 3.1.7 6 continuation bytes: "" | 3.1.8 7 continuation bytes: "" | | 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf): | | " | | | " | | 3.2 Lonely start characters | | 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf), | each followed by a space character: | | " | " | | 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef), | each followed by a space character: | | " " | | 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7), | each followed by a space character: | | " " | | 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb), | each followed by a space character: | | " " | | 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd), | each followed by a space character: | | " " | | 3.3 Sequences with last continuation byte missing | | All bytes of an incomplete sequence should be signalled as a single | malformed sequence, i.e., you should see only a single replacement | character in each of the next 10 tests. (Characters as in section 2) | | 3.3.1 2-byte sequence with last byte missing (U+0000): "" | 3.3.2 3-byte sequence with last byte missing (U+0000): "" | 3.3.3 4-byte sequence with last byte missing (U+0000): "" | 3.3.4 5-byte sequence with last byte missing (U+0000): "" | 3.3.5 6-byte sequence with last byte missing (U+0000): "" | 3.3.6 2-byte sequence with last byte missing (U-000007FF): "" | 3.3.7 3-byte sequence with last byte missing (U-0000FFFF): "" | 3.3.8 4-byte sequence with last byte missing (U-001FFFFF): "" | 3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): "" | 3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): "" | | 3.4 Concatenation of incomplete sequences | | All the 10 sequences of 3.3 concatenated, you should see 10 malformed | sequences being signalled: | | "" | | 3.5 Impossible bytes | | The following two bytes cannot appear in a correct UTF-8 string | | 3.5.1 fe = "" | 3.5.2 ff = "" | 3.5.3 fe fe ff ff = "" | | 4 Overlong sequences | | The following sequences are not malformed according to the letter of | the Unicode 2.0 standard. However, they are longer then necessary and | a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8 | decoder" should reject them just like malformed sequences for two | reasons: (1) It helps to debug applications if overlong sequences are | not treated as valid representations of characters, because this helps | to spot problems more quickly. (2) Overlong sequences provide | alternative representations of characters, that could maliciously be | used to bypass filters that check only for ASCII characters. For | instance, a 2-byte encoded line feed (LF) would not be caught by a | line counter that counts only 0x0a bytes, but it would still be | processed as a line feed by an unsafe UTF-8 decoder later in the | pipeline. From a security point of view, ASCII compatibility of UTF-8 | sequences means also, that ASCII characters are *only* allowed to be | represented by ASCII bytes in the range 0x00-0x7f. To ensure this | aspect of ASCII compatibility, use only "safe UTF-8 decoders" that | reject overlong UTF-8 sequences for which a shorter encoding exists. | | 4.1 Examples of an overlong ASCII character | | With a safe UTF-8 decoder, all of the following five overlong | representations of the ASCII character slash ("/") should be rejected | like a malformed UTF-8 sequence, for instance by substituting it with | a replacement character. If you see a slash below, you do not have a | safe UTF-8 decoder! | | 4.1.1 U+002F = c0 af = "" | 4.1.2 U+002F = e0 80 af = "" | 4.1.3 U+002F = f0 80 80 af = "" | 4.1.4 U+002F = f8 80 80 80 af = "" | 4.1.5 U+002F = fc 80 80 80 80 af = "" | | 4.2 Maximum overlong sequences | | Below you see the highest Unicode value that is still resulting in an | overlong sequence if represented with the given number of bytes. This | is a boundary test for safe UTF-8 decoders. All five characters should | be rejected like malformed UTF-8 sequences. | | 4.2.1 U-0000007F = c1 bf = "" | 4.2.2 U-000007FF = e0 9f bf = "" | 4.2.3 U-0000FFFF = f0 8f bf bf = "" | 4.2.4 U-001FFFFF = f8 87 bf bf bf = "" | 4.2.5 U-03FFFFFF = fc 83 bf bf bf bf = "" | | 4.3 Overlong representation of the NUL character | | The following five sequences should also be rejected like malformed | UTF-8 sequences and should not be treated like the ASCII NUL | character. | | 4.3.1 U+0000 = c0 80 = "" | 4.3.2 U+0000 = e0 80 80 = "" | 4.3.3 U+0000 = f0 80 80 80 = "" | 4.3.4 U+0000 = f8 80 80 80 80 = "" | 4.3.5 U+0000 = fc 80 80 80 80 80 = "" | | 5 Illegal code positions | | The following UTF-8 sequences should be rejected like malformed | sequences, because they never represent valid ISO 10646 characters and | a UTF-8 decoder that accepts them might introduce security problems | comparable to overlong UTF-8 sequences. | | 5.1 Single UTF-16 surrogates | | 5.1.1 U+D800 = ed a0 80 = "" | 5.1.2 U+DB7F = ed ad bf = "" | 5.1.3 U+DB80 = ed ae 80 = "" | 5.1.4 U+DBFF = ed af bf = "" | 5.1.5 U+DC00 = ed b0 80 = "" | 5.1.6 U+DF80 = ed be 80 = "" | 5.1.7 U+DFFF = ed bf bf = "" | | 5.2 Paired UTF-16 surrogates | | 5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = "" | 5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = "" | 5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = "" | 5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = "" | 5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = "" | 5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = "" | 5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = "" | 5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = "" | | | THE END | ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/test_unchecked_api.h�����������������������������������������������0000664�0000000�0000000�00000011635�14411236400�0022515�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef UTF8_FOR_CPP_TEST_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_TEST_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "utf8/unchecked.h" #include <string> #include <vector> using namespace std; TEST(UnCheckedAPITests, test_append) { unsigned char u[5] = {0,0,0,0,0}; utf8::unchecked::append(0x0448, u); EXPECT_EQ (u[0], 0xd1); EXPECT_EQ (u[1], 0x88); EXPECT_EQ (u[2], 0); EXPECT_EQ (u[3], 0); EXPECT_EQ (u[4], 0); utf8::unchecked::append(0x65e5, u); EXPECT_EQ (u[0], 0xe6); EXPECT_EQ (u[1], 0x97); EXPECT_EQ (u[2], 0xa5); EXPECT_EQ (u[3], 0); EXPECT_EQ (u[4], 0); utf8::unchecked::append(0x3044, u); EXPECT_EQ (u[0], 0xe3); EXPECT_EQ (u[1], 0x81); EXPECT_EQ (u[2], 0x84); EXPECT_EQ (u[3], 0); EXPECT_EQ (u[4], 0); utf8::unchecked::append(0x10346, u); EXPECT_EQ (u[0], 0xf0); EXPECT_EQ (u[1], 0x90); EXPECT_EQ (u[2], 0x8d); EXPECT_EQ (u[3], 0x86); EXPECT_EQ (u[4], 0); } TEST(UnCheckedAPITests, test_next) { const char* twochars = "\xe6\x97\xa5\xd1\x88"; const char* w = twochars; unsigned int cp = utf8::unchecked::next(w); EXPECT_EQ (cp, 0x65e5); EXPECT_EQ (w, twochars + 3); const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; w = threechars; cp = utf8::unchecked::next(w); EXPECT_EQ (cp, 0x10346); EXPECT_EQ (w, threechars + 4); cp = utf8::unchecked::next(w); EXPECT_EQ (cp, 0x65e5); EXPECT_EQ (w, threechars + 7); cp = utf8::unchecked::next(w); EXPECT_EQ (cp, 0x0448); EXPECT_EQ (w, threechars + 9); } TEST(UnCheckedAPITests, test_peek_next) { const char* const cw = "\xe6\x97\xa5\xd1\x88"; unsigned int cp = utf8::unchecked::peek_next(cw); EXPECT_EQ (cp, 0x65e5); } TEST(UnCheckedAPITests, test_prior) { const char* twochars = "\xe6\x97\xa5\xd1\x88"; const char* w = twochars + 3; unsigned int cp = utf8::unchecked::prior (w); EXPECT_EQ (cp, 0x65e5); EXPECT_EQ (w, twochars); const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; w = threechars + 9; cp = utf8::unchecked::prior(w); EXPECT_EQ (cp, 0x0448); EXPECT_EQ (w, threechars + 7); cp = utf8::unchecked::prior(w); EXPECT_EQ (cp, 0x65e5); EXPECT_EQ (w, threechars + 4); cp = utf8::unchecked::prior(w); EXPECT_EQ (cp, 0x10346); EXPECT_EQ (w, threechars); } TEST(UnCheckedAPITests, test_advance) { const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; const char* w = threechars; utf8::unchecked::advance(w, 2); EXPECT_EQ(w, threechars + 7); utf8::unchecked::advance(w, -2); EXPECT_EQ(w, threechars); utf8::unchecked::advance(w, 3); EXPECT_EQ(w, threechars + 9); utf8::unchecked::advance(w, -2); EXPECT_EQ(w, threechars + 4); utf8::unchecked::advance(w, -1); EXPECT_EQ(w, threechars); } TEST(UnCheckedAPITests, test_distance) { const char* twochars = "\xe6\x97\xa5\xd1\x88"; size_t dist = static_cast<size_t>(utf8::unchecked::distance(twochars, twochars + 5)); EXPECT_EQ (dist, 2); } TEST(UnCheckedAPITests, test_utf32to8) { unsigned int utf32string[] = {0x448, 0x65E5, 0x10346, 0}; string utf8result; utf8::unchecked::utf32to8(utf32string, utf32string + 3, back_inserter(utf8result)); EXPECT_EQ (utf8result.size(), 9); } TEST(UnCheckedAPITests, test_utf8to32) { const char* twochars = "\xe6\x97\xa5\xd1\x88"; vector<unsigned int> utf32result; utf8::unchecked::utf8to32(twochars, twochars + 5, back_inserter(utf32result)); EXPECT_EQ (utf32result.size(), 2); } TEST(UnCheckedAPITests, test_utf16to8) { unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; string utf8result; utf8::unchecked::utf16to8(utf16string, utf16string + 5, back_inserter(utf8result)); EXPECT_EQ (utf8result.size(), 10); } TEST(UnCheckedAPITests, test_utf8to16) { char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; vector <unsigned short> utf16result; utf8::unchecked::utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result)); EXPECT_EQ (utf16result.size(), 4); EXPECT_EQ (utf16result[2], 0xd834); EXPECT_EQ (utf16result[3], 0xdd1e); } TEST(UnCheckedAPITests, test_replace_invalid) { char invalid_sequence[] = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; vector<char> replace_invalid_result; utf8::unchecked::replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), std::back_inserter(replace_invalid_result), '?'); bool bvalid = utf8::is_valid(replace_invalid_result.begin(), replace_invalid_result.end()); EXPECT_TRUE (bvalid); const char fixed_invalid_sequence[] = "a????z"; EXPECT_EQ (sizeof(fixed_invalid_sequence), replace_invalid_result.size()); EXPECT_TRUE (std::equal(replace_invalid_result.begin(), replace_invalid_result.begin() + sizeof(fixed_invalid_sequence), fixed_invalid_sequence)); } #endif ���������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/tests/test_unchecked_iterator.h������������������������������������������0000664�0000000�0000000�00000002140�14411236400�0023564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef UTF8_FOR_CPP_TEST_UNCHECKED_ITERATOR_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_TEST_UNCHECKED_ITERATOR_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "utf8/unchecked.h" using namespace utf8::unchecked; TEST(UnCheckedIteratrTests, test_increment) { const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; utf8::unchecked::iterator<const char*> it(threechars); utf8::unchecked::iterator<const char*> it2 = it; EXPECT_EQ (it2, it); EXPECT_EQ (*it, 0x10346); EXPECT_EQ (*(++it), 0x65e5); EXPECT_EQ ((*it++), 0x65e5); EXPECT_EQ (*it, 0x0448); EXPECT_NE (it, it2); utf8::unchecked::iterator<const char*> endit (threechars + 9); EXPECT_EQ (++it, endit); } TEST(UnCheckedIteratrTests, test_decrement) { const char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; utf8::unchecked::iterator<const char*> it(threechars+9); EXPECT_EQ (*(--it), 0x0448); EXPECT_EQ ((*it--), 0x0448); EXPECT_EQ (*it, 0x65e5); EXPECT_EQ (--it, utf8::unchecked::iterator<const char*>(threechars)); EXPECT_EQ (*it, 0x10346); } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/lib/utfcpp/v3/utf8cppConfig.cmake.in���������������������������������������������������0000664�0000000�0000000�00000000227�14411236400�0021502�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/utf8cppTargets.cmake") check_required_components( "utf8cpp" ) add_library(utf8::cpp ALIAS utf8cpp) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/python/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0014275�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/python/__init__.py���������������������������������������������������������������������0000664�0000000�0000000�00000000000�14411236400�0016374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/python/demo.py�������������������������������������������������������������������������0000775�0000000�0000000�00000024757�14411236400�0015615�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python import sys from datetime import datetime # The following literate program will demonstrate, by example, how to use the # Ledger Python module to access your data and build custom reports using the # magic of Python. import ledger print("Welcome to the Ledger.Python demo!") # Some quick helper functions to help us assert various types of truth # throughout the script. def assertEqual(pat, candidate): if pat != candidate: raise Exception("FAILED: %s != %s" % (pat, candidate)) sys.exit(1) ############################################################################### # # COMMODITIES # # Every amount in Ledger has a commodity, even if it is the "null commodity". # What's special about commodities are not just their symbol, but how they # alter the way amounts are displayed. # # For example, internally Ledger uses infinite precision rational numbers, # which have no decimal point. So how does it know that $1.00 / $0.75 should # be displayed as $1.33, and not with an infinitely repeating decimal? It # does it by consulting the commodity. # # Whenever an amount is encountered in your data file, Ledger observes how you # specified it: # - How many digits of precision did you use? # - Was the commodity name before or after the amount? # - Was the commodity separated from the amount by a space? # - Did you use thousands markers (1,000)? # - Did you use European-style numbers (1.000,00)? # # By tracking this information for each commodity, Ledger knows how you want # to see the amount in your reports. This way, dollars can be output as # $123.56, while stock options could be output as 10.113 AAPL. # # Your program can access the known set of commodities using the global # `ledger.commodities'. This object behaves like a dict, and support all of # the non-modifying dict protocol methods. If you wish to create a new # commodity without parsing an amount, you can use the method # `find_or_create': comms = ledger.commodities usd = comms.find_or_create('$') xcd = comms.find_or_create('XCD') # Tests currency symbols encoded using UCS. For details see #2132. eur = comms.find_or_create('€') # UCS-2 / UCS-4 xxx = comms.find_or_create('¤') # UCS-1 assert not comms.find('CAD') assert not comms.has_key('CAD') assert not 'CAD' in comms # The above mentioned commodity display attributes can be set using commodity # display flags. This is not something you will usually be doing, however, as # these flags can be inferred correctly from a large enough set of sample # amounts, such as those found in your data file. If you live in Europe and # want all amounts to default to the European-style, set the static variable # `european_by_default'. eur.add_flags(ledger.COMMODITY_STYLE_DECIMAL_COMMA) assert eur.has_flags(ledger.COMMODITY_STYLE_DECIMAL_COMMA) assert not eur.has_flags(ledger.COMMODITY_STYLE_THOUSANDS) comms.european_by_default = True # There are a few built-in commodities: null, %, h, m and s. Normally you # don't need to worry about them, but they'll show up if you examine all the # keys in the commodities dict. assertEqual([u'', u'$', u'%', u'XCD', u'h', u'm', u's', u'¤', u'€'], sorted(comms.keys())) # All the styles of dict iteration are supported: for symbol in comms.iterkeys(): pass for commodity in comms.itervalues(): pass #for symbol, commodity in comms.iteritems(): # pass #for symbol, commodity in comms: # pass # Another important thing about commodities is that they remember if they've # been exchanged for another commodity, and what the conversion rate was on # that date. You can record specific conversion rates for any date using the # `exchange' method. comms.exchange(eur, ledger.Amount('$0.77')) # Trade 1 € for $0.77 comms.exchange(eur, ledger.Amount('$0.66'), datetime.now()) # For the most part, however, you won't be interacting with commodities # directly, except maybe to look at their `symbol'. assertEqual('$', usd.symbol) assertEqual('$', comms['$'].symbol) ############################################################################### # # AMOUNTS & BALANCES # # Ledger deals with two basic numerical values: Amount and Balance objects. # An Amount is an infinite-precision rational with an associated commodity # (even if it is the null commodity, which is called an "uncommoditized # amount"). A Balance is a collection of Amounts of differing commodities. # # Amounts support all the math operations you might expect of an integer, # except it carries a commodity. Let's take dollars for example: zero = ledger.Amount("$0") one = ledger.Amount("$1") oneb = ledger.Amount("$1") two = ledger.Amount("$2") three = ledger.Amount("3") # uncommoditized assert one == oneb # numeric equality, not identity assert one != two assert not zero # tests if it would *display* as a zero assert one < two assert one > zero # For addition and subtraction, only amounts of the same commodity may be # used, unless one of the amounts has no commodity at all -- in which case the # result uses the commodity of the other value. Adding $10 to 10 €, for # example, causes an ArithmeticError exception, but adding 10 to $10 gives # $20. four = ledger.Amount(two) # make a copy four += two assertEqual(four, two + two) assertEqual(zero, one - one) try: two += ledger.Amount("20 €") assert False except ArithmeticError: pass # Use `number' to get the uncommoditized version of an Amount assertEqual(three, (two + one).number()) # Multiplication and division does supports Amounts of different commodities, # however: # - If either amount is uncommoditized, the result carries the commodity of # the other amount. # - Otherwise, the result always carries the commodity of the first amount. five = ledger.Amount("5 CAD") assertEqual(one, two / two) assertEqual(five, (five * ledger.Amount("$2")) - ledger.Amount("5")) # An amount's commodity determines the decimal precision it's displayed with. # However, this "precision" is a notional thing only. You can tell an amount # to ignore its display precision by setting `keep_precision' to True. # (Uncommoditized amounts ignore the value of `keep_precision', and assume it # is always True). In this case, Ledger does its best to maintain maximal # precision by watching how the Amount is used. That is, 1.01 * 1.01 yields a # precision of 4. This tracking is just a best estimate, however, since # internally Ledger never uses floating-point values. amt = ledger.Amount('$100.12') mini = ledger.Amount('0.00045') assert not amt.keep_precision assertEqual(5, mini.precision) assertEqual(5, mini.display_precision) # display_precision == precision assertEqual(2, amt.precision) assertEqual(2, amt.display_precision) mini *= mini amt *= amt assertEqual(10, mini.precision) assertEqual(10, mini.display_precision) assertEqual(4, amt.precision) assertEqual(2, amt.display_precision) # There are several other supported math operations: amt = ledger.Amount('$100.12') market = ((ledger.Amount('1 €') / ledger.Amount('$0.77')) * amt) assertEqual(market, amt.value(eur)) # find present market value assertEqual('$-100.12', str(amt.negated())) # negate the amount assertEqual('$-100.12', str(- amt)) # negate it more simply assertEqual('$0.01', str(amt.inverted())) # reverse NUM/DEM assertEqual('$100.12', str(amt.rounded())) # round it to display precision assertEqual('$100.12', str(amt.truncated())) # truncate to display precision assertEqual('$100.00', str(amt.floored())) # floor it to nearest integral assertEqual('$100.12', str(abs(amt))) # absolute value assertEqual('$100.12', str(amt)) # render to a string assertEqual('100.12', amt.quantity_string()) # render quantity to a string assertEqual('100.12', str(amt.number())) # strip away commodity assertEqual(1, amt.sign()) # -1, 0 or 1 assert amt.is_nonzero() # True if display amount nonzero assert not amt.is_zero() # True if display amount is zero assert not amt.is_realzero() # True only if value is 0/0 assert not amt.is_null() # True if uninitialized # Amounts can also be converted the standard floats and integers, although # this is not recommend since it can lose precision. assertEqual(100.12, amt.to_double()) assert amt.fits_in_long() # there is no `fits_in_double' assertEqual(100, amt.to_long()) # Finally, amounts can be annotated to provide additional information about # "lots" of a given commodity. This example shows $100.12 that was purchased # on 2009/10/01 for 140 €. Lot information can be accessed through via the # Amount's `annotation' property. You can also strip away lot details to get # the underlying amount. If you want the total price of any Amount, by # multiplying by its per-unit lot price, call the `Amount.price' method # instead of the `Annotation.price' property. amt2 = ledger.Amount('$100.12 {140 €} [2009/10/01]') assert amt2.has_annotation() assertEqual(amt, amt2.strip_annotations()) assertEqual(ledger.Amount('140 €'), amt2.annotation.price) assertEqual(ledger.Amount('14016,8 €'), amt2.price()) # european amount! ############################################################################### # # VALUES # # As common as Amounts and Balances are, there is a more prevalent numeric # type you will encounter when generating reports: Value objects. A Value is # a variadic type that can be any of the following types: # - Amount # - Balance # - boolean # - integer # - datetime # - date # - string # - regex # - sequence # # The reason for the variadic type is that it supports dynamic self-promotion. # For example, it is illegal to add two Amounts of different commodities, but # it is not illegal to add two Value amounts of different commodities. In the # former case an exception in raised, but in the latter the Value simply # promotes itself to a Balance object to make the addition valid. # # Values are not used by any of Ledger's data objects (Journal, Transaction, # Posting or Account), but they are used extensively by value expressions. val = ledger.Value('$100.00') assert val.is_amount() assertEqual('$', val.to_amount().commodity.symbol) # JOURNALS #journal.find_account('') #journal.find_or_create_account('') # ACCOUNTS #account.name #account.fullname() #account.amount #account.total # TRANSACTIONS #txn.payee # POSTINGS #post.account # REPORTING #journal.collect('-M food') #journal.collect_accounts('^assets ^liab ^equity') print('Demo completed successfully.') �����������������ledger-3.3.2/shell.nix������������������������������������������������������������������������������0000664�0000000�0000000�00000000161�14411236400�0014601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������(import (fetchTarball https://github.com/edolstra/flake-compat/archive/master.tar.gz) { src = ./.; }).shellNix ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/�����������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0013543�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/CMakeLists.txt���������������������������������������������������������������������0000664�0000000�0000000�00000020541�14411236400�0016305�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������set(LEDGER_CLI_SOURCES global.cc main.cc) set(LEDGER_SOURCES stats.cc generate.cc csv.cc convert.cc draft.cc emacs.cc ptree.cc print.cc output.cc precmd.cc chain.cc filters.cc report.cc views.cc select.cc session.cc option.cc lookup.cc compare.cc iterators.cc timelog.cc textual.cc temps.cc journal.cc account.cc xact.cc post.cc item.cc format.cc query.cc scope.cc expr.cc op.cc parser.cc token.cc value.cc balance.cc quotes.cc history.cc pool.cc annotate.cc commodity.cc amount.cc stream.cc mask.cc times.cc error.cc utils.cc wcwidth.cc) if (HAVE_GPGME) list(APPEND LEDGER_SOURCES gpgme.cc) endif() if (HAVE_BOOST_PYTHON) list(APPEND LEDGER_SOURCES py_account.cc py_amount.cc py_balance.cc py_commodity.cc py_expr.cc py_format.cc py_item.cc py_journal.cc py_post.cc py_session.cc py_times.cc py_utils.cc py_value.cc py_xact.cc pyinterp.cc pyledger.cc) endif() set(LEDGER_INCLUDES account.h amount.h annotate.h balance.h chain.h commodity.h compare.h context.h convert.h csv.h draft.h emacs.h error.h expr.h exprbase.h filters.h flags.h format.h generate.h global.h gpgme.h history.h item.h iterators.h journal.h lookup.h mask.h op.h option.h output.h parser.h pool.h post.h precmd.h predicate.h print.h pstream.h ptree.h pyinterp.h pyutils.h query.h quotes.h report.h scope.h select.h session.h stats.h stream.h temps.h timelog.h times.h token.h unistring.h utils.h value.h views.h xact.h ${PROJECT_BINARY_DIR}/system.hh) # Windows provides no strptime(), so supply our own. if (WIN32 OR CYGWIN) list(APPEND LEDGER_INCLUDES strptime.h) list(APPEND LEDGER_SOURCES strptime.cc) endif() if (CMAKE_BUILD_TYPE STREQUAL "Debug") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") add_definitions( # -Weverything # -Wno-disabled-macro-expansion # -Wno-padded # -Wno-weak-vtables # -Wno-exit-time-destructors # -Wno-global-constructors # -Wno-switch-enum # -Wno-missing-prototypes # -Wno-missing-noreturn # -Wno-unused-parameter # -Wno-c++98-compat # -fno-limit-debug-info -Wno-\#pragma-messages -Wno-unused-local-typedef --system-header-prefix=include/boost/ --system-header-prefix=boost/) macro(ADD_PCH_RULE _header_filename _src_list _other_srcs) set(_pch_filename "${_header_filename}.pch") set_source_files_properties( ${${_src_list}} PROPERTIES COMPILE_FLAGS "-include ${_header_filename}") if (_other_srcs) set_source_files_properties( ${_other_srcs} PROPERTIES COMPILE_FLAGS "-include ${_header_filename}") endif() list(APPEND ${_src_list} ${_pch_filename}) set(_args ${CMAKE_CXX_FLAGS}) list(APPEND _args ${CMAKE_CXX_FLAGS_DEBUG}) if (BUILD_LIBRARY) list(APPEND _args ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}) endif() list(APPEND _args "-std=c++11 ") if (CYGWIN) list(APPEND _args "-U__STRICT_ANSI__") endif() list(APPEND _args "-x c++-header " ${_inc}) list(APPEND _args -c ${_header_filename} -o ${_pch_filename}) get_directory_property(DIRINC INCLUDE_DIRECTORIES) foreach(_inc ${DIRINC}) list(APPEND _args "-isystem " ${_inc}) endforeach(_inc ${DIRINC}) separate_arguments(_args) add_custom_command(OUTPUT ${_pch_filename} COMMAND rm -f ${_pch_filename} COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${_args} DEPENDS ${_header_filename}) endmacro(ADD_PCH_RULE _header_filename _src_list _other_srcs) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(GXX_WARNING_FLAGS -pedantic -Wall -Winvalid-pch -Wextra -Wcast-align -Wcast-qual -Wfloat-equal -Wmissing-field-initializers -Wno-endif-labels -Wno-overloaded-virtual -Wsign-compare -Wsign-promo -Wwrite-strings -Wno-unused-parameter -Wno-old-style-cast -Wno-deprecated -Wno-strict-aliasing) add_definitions(${GXX_WARNING_FLAGS}) macro(ADD_PCH_RULE _header_filename _src_list _other_srcs) set(_gch_filename "${_header_filename}.gch") set_source_files_properties( ${${_src_list}} PROPERTIES COMPILE_FLAGS "-Winvalid-pch") if (_other_srcs) set_source_files_properties( ${_other_srcs} PROPERTIES COMPILE_FLAGS "-Winvalid-pch") endif() list(APPEND ${_src_list} ${_gch_filename}) set(_args ${CMAKE_CXX_FLAGS}) list(APPEND _args ${CMAKE_CXX_FLAGS_DEBUG}) if (BUILD_LIBRARY) list(APPEND _args ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}) endif() list(APPEND _args ${GXX_WARNING_FLAGS}) list(APPEND _args "-std=c++11 ") if (CYGWIN) list(APPEND _args "-U__STRICT_ANSI__") endif() list(APPEND _args "-x c++-header " ${_inc}) list(APPEND _args -c ${_header_filename} -o ${_gch_filename}) get_directory_property(DIRINC INCLUDE_DIRECTORIES) foreach(_inc ${DIRINC}) list(APPEND _args "-I" ${_inc}) endforeach(_inc ${DIRINC}) separate_arguments(_args) add_custom_command(OUTPUT ${_gch_filename} COMMAND rm -f ${_gch_filename} COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${_args} DEPENDS ${_header_filename}) endmacro(ADD_PCH_RULE _header_filename _src_list _other_srcs) else() macro(ADD_PCH_RULE _header_filename _src_list _other_srcs) endmacro(ADD_PCH_RULE _header_filename _src_list _other_srcs) endif() else() macro(ADD_PCH_RULE _header_filename _src_list _other_srcs) endmacro(ADD_PCH_RULE _header_filename _src_list _other_srcs) endif() if(PRECOMPILE_SYSTEM_HH AND NOT (COMMAND target_precompile_headers)) # enable fallback for CMake versions older than 3.16 without target_precompile_headers add_pch_rule(${PROJECT_BINARY_DIR}/system.hh LEDGER_SOURCES LEDGER_CLI_SOURCES) endif() include(GNUInstallDirs) if (BUILD_LIBRARY) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") add_library(libledger SHARED ${LEDGER_SOURCES}) add_ledger_library_dependencies(libledger) set_target_properties(libledger PROPERTIES PREFIX "" INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" VERSION ${Ledger_VERSION_MAJOR} SOVERSION ${Ledger_VERSION_MAJOR}) add_executable(ledger main.cc global.cc) target_link_libraries(ledger libledger) if (HAVE_GPGME) target_link_libraries(ledger Gpgmepp) endif() if (HAVE_BOOST_PYTHON) target_link_libraries(ledger ${Python_LIBRARIES}) endif() install(TARGETS libledger DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES ${LEDGER_INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ledger) else() add_executable(ledger ${LEDGER_SOURCES} main.cc global.cc) add_ledger_library_dependencies(ledger) endif() if (PRECOMPILE_SYSTEM_HH AND (COMMAND target_precompile_headers) AND (CMAKE_BUILD_TYPE STREQUAL "Debug")) if (BUILD_LIBRARY) target_precompile_headers(libledger PRIVATE ${PROJECT_BINARY_DIR}/system.hh) target_precompile_headers(ledger REUSE_FROM libledger) else() target_precompile_headers(ledger PRIVATE ${PROJECT_BINARY_DIR}/system.hh) endif() endif() if (USE_PYTHON) if (Python_SITEARCH) if (WIN32 AND NOT CYGWIN) set(_ledger_python_module_name "ledger.pyd") elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin) set(_ledger_python_module_name "ledger.so") else() set(_ledger_python_module_name "ledger${CMAKE_SHARED_LIBRARY_SUFFIX}") endif() # FIXME: symlink would be sufficient: # maybe using install(CODE "...") and # execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink ...). # Windows will need a special case due to not supporting symlinks. add_custom_command( TARGET libledger POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:libledger> "${CMAKE_BINARY_DIR}/${_ledger_python_module_name}") install( FILES "${CMAKE_BINARY_DIR}/${_ledger_python_module_name}" DESTINATION ${Python_SITEARCH}) else() message(WARNING "Python_SITEARCH not set. Will not install python module.") endif() endif() install(TARGETS ledger DESTINATION ${CMAKE_INSTALL_BINDIR}) ### CMakeLists.txt ends here ���������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/account.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000054371�14411236400�0015520�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "account.h" #include "post.h" #include "xact.h" namespace ledger { account_t::~account_t() { TRACE_DTOR(account_t); foreach (accounts_map::value_type& pair, accounts) { if (! pair.second->has_flags(ACCOUNT_TEMP) || has_flags(ACCOUNT_TEMP)) { checked_delete(pair.second); } } } account_t * account_t::find_account(const string& acct_name, const bool auto_create) { accounts_map::const_iterator i = accounts.find(acct_name); if (i != accounts.end()) return (*i).second; char buf[8192]; string::size_type sep = acct_name.find(':'); assert(sep < 256|| sep == string::npos); const char * first, * rest; if (sep == string::npos) { first = acct_name.c_str(); rest = NULL; } else { std::strncpy(buf, acct_name.c_str(), sep); buf[sep] = '\0'; first = buf; rest = acct_name.c_str() + sep + 1; } account_t * account; i = accounts.find(first); if (i == accounts.end()) { if (! auto_create) return NULL; account = new account_t(this, first); // An account created within a temporary or generated account is itself // temporary or generated, so that the whole tree has the same status. if (has_flags(ACCOUNT_TEMP)) account->add_flags(ACCOUNT_TEMP); if (has_flags(ACCOUNT_GENERATED)) account->add_flags(ACCOUNT_GENERATED); #if DEBUG_ON std::pair<accounts_map::iterator, bool> result = #endif accounts.insert(accounts_map::value_type(first, account)); #if DEBUG_ON assert(result.second); #endif } else { account = (*i).second; } if (rest) account = account->find_account(rest, auto_create); return account; } namespace { account_t * find_account_re_(account_t * account, const mask_t& regexp) { if (regexp.match(account->fullname())) return account; foreach (accounts_map::value_type& pair, account->accounts) if (account_t * a = find_account_re_(pair.second, regexp)) return a; return NULL; } } account_t * account_t::find_account_re(const string& regexp) { return find_account_re_(this, mask_t(regexp)); } void account_t::add_post(post_t * post) { posts.push_back(post); // Adding a new post changes the possible totals that may have been // computed before. if (xdata_) { xdata_->self_details.gathered = false; xdata_->self_details.calculated = false; xdata_->family_details.gathered = false; xdata_->family_details.calculated = false; if (! xdata_->family_details.total.is_null()) { xdata_->family_details.total = ledger::value_t(); } account_t *ancestor = this; while (ancestor->parent) { ancestor = ancestor->parent; if (ancestor->has_xdata()) { xdata_t &xdata = ancestor->xdata(); xdata.family_details.gathered = false; xdata.family_details.calculated = false; xdata.family_details.total = ledger::value_t(); } } } } void account_t::add_deferred_post(const string& uuid, post_t * post) { if (! deferred_posts) deferred_posts = deferred_posts_map_t(); deferred_posts_map_t::iterator i = deferred_posts->find(uuid); if (i == deferred_posts->end()) { posts_list lst; lst.push_back(post); deferred_posts->insert(deferred_posts_map_t::value_type(uuid, lst)); } else { (*i).second.push_back(post); } } void account_t::apply_deferred_posts() { if (deferred_posts) { foreach (deferred_posts_map_t::value_type& pair, *deferred_posts) { foreach (post_t * post, pair.second) post->account->add_post(post); } deferred_posts = none; } // Also apply in child accounts foreach (const accounts_map::value_type& pair, accounts) pair.second->apply_deferred_posts(); } bool account_t::remove_post(post_t * post) { // It's possible that 'post' wasn't yet in this account, but try to // remove it anyway. This can happen if there is an error during // parsing, when the posting knows what it's account is, but // xact_t::finalize has not yet added that posting to the account. posts.remove(post); post->account = NULL; return true; } string account_t::fullname() const { if (! _fullname.empty()) { return _fullname; } else { const account_t * first = this; string fullname = name; while (first->parent) { first = first->parent; if (! first->name.empty()) fullname = first->name + ":" + fullname; } _fullname = fullname; return fullname; } } string account_t::partial_name(bool flat) const { string pname = name; for (const account_t * acct = parent; acct && acct->parent; acct = acct->parent) { if (! flat) { std::size_t count = acct->children_with_flags(ACCOUNT_EXT_TO_DISPLAY); assert(count > 0); if (count > 1 || acct->has_xflags(ACCOUNT_EXT_TO_DISPLAY)) break; } pname = acct->name + ":" + pname; } return pname; } std::ostream& operator<<(std::ostream& out, const account_t& account) { out << account.fullname(); return out; } namespace { value_t get_partial_name(call_scope_t& args) { return string_value(args.context<account_t>() .partial_name(args.has<bool>(0) && args.get<bool>(0))); } value_t get_account(call_scope_t& args) { // this gets the name account_t& account(args.context<account_t>()); if (args.has<string>(0)) { account_t * acct = account.parent; for (; acct && acct->parent; acct = acct->parent) ; if (args[0].is_string()) return scope_value(acct->find_account(args.get<string>(0), false)); else if (args[0].is_mask()) return scope_value(acct->find_account_re(args.get<mask_t>(0).str())); else return NULL_VALUE; } else if (args.type_context() == value_t::SCOPE) { return scope_value(&account); } else { return string_value(account.fullname()); } } value_t get_account_base(account_t& account) { return string_value(account.name); } value_t get_amount(account_t& account) { return SIMPLIFIED_VALUE_OR_ZERO(account.amount()); } value_t get_total(account_t& account) { return SIMPLIFIED_VALUE_OR_ZERO(account.total()); } value_t get_subcount(account_t& account) { return long(account.self_details().posts_count); } value_t get_count(account_t& account) { return long(account.family_details().posts_count); } value_t get_cost(account_t&) { throw_(calc_error, _("An account does not have a 'cost' value")); return false; } value_t get_depth(account_t& account) { return long(account.depth); } value_t get_note(account_t& account) { return account.note ? string_value(*account.note) : NULL_VALUE; } value_t ignore(account_t&) { return false; } value_t get_true(account_t&) { return true; } value_t get_addr(account_t& account) { return long(reinterpret_cast<intptr_t>(&account)); } value_t get_depth_parent(account_t& account) { std::size_t depth = 0; for (const account_t * acct = account.parent; acct && acct->parent; acct = acct->parent) { std::size_t count = acct->children_with_flags(ACCOUNT_EXT_TO_DISPLAY); assert(count > 0); if (count > 1 || acct->has_xflags(ACCOUNT_EXT_TO_DISPLAY)) depth++; } return long(depth); } value_t get_depth_spacer(account_t& account) { std::size_t depth = 0; for (const account_t * acct = account.parent; acct && acct->parent; acct = acct->parent) { std::size_t count = acct->children_with_flags(ACCOUNT_EXT_TO_DISPLAY); assert(count > 0); if (count > 1 || acct->has_xflags(ACCOUNT_EXT_TO_DISPLAY)) depth++; } std::ostringstream out; for (std::size_t i = 0; i < depth; i++) out << " "; return string_value(out.str()); } value_t get_latest_cleared(account_t& account) { return account.self_details().latest_cleared_post; } value_t get_earliest(account_t& account) { return account.self_details().earliest_post; } value_t get_earliest_checkin(account_t& account) { return (! account.self_details().earliest_checkin.is_not_a_date_time() ? value_t(account.self_details().earliest_checkin) : NULL_VALUE); } value_t get_latest(account_t& account) { return account.self_details().latest_post; } value_t get_latest_checkout(account_t& account) { return (! account.self_details().latest_checkout.is_not_a_date_time() ? value_t(account.self_details().latest_checkout) : NULL_VALUE); } value_t get_latest_checkout_cleared(account_t& account) { return account.self_details().latest_checkout_cleared; } template <value_t (*Func)(account_t&)> value_t get_wrapper(call_scope_t& args) { return (*Func)(args.context<account_t>()); } value_t get_parent(account_t& account) { return scope_value(account.parent); } value_t fn_any(call_scope_t& args) { account_t& account(args.context<account_t>()); expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(0)); foreach (post_t * p, account.posts) { bind_scope_t bound_scope(args, *p); if (expr->calc(bound_scope, args.locus, args.depth).to_boolean()) return true; } return false; } value_t fn_all(call_scope_t& args) { account_t& account(args.context<account_t>()); expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(0)); foreach (post_t * p, account.posts) { bind_scope_t bound_scope(args, *p); if (! expr->calc(bound_scope, args.locus, args.depth).to_boolean()) return false; } return true; } } expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind, const string& fn_name) { if (kind != symbol_t::FUNCTION) return NULL; switch (fn_name[0]) { case 'a': if (fn_name[1] == '\0' || fn_name == "amount") return WRAP_FUNCTOR(get_wrapper<&get_amount>); else if (fn_name == "account") return WRAP_FUNCTOR(&get_account); else if (fn_name == "account_base") return WRAP_FUNCTOR(get_wrapper<&get_account_base>); else if (fn_name == "addr") return WRAP_FUNCTOR(get_wrapper<&get_addr>); else if (fn_name == "any") return WRAP_FUNCTOR(&fn_any); else if (fn_name == "all") return WRAP_FUNCTOR(&fn_all); break; case 'c': if (fn_name == "count") return WRAP_FUNCTOR(get_wrapper<&get_count>); else if (fn_name == "cost") return WRAP_FUNCTOR(get_wrapper<&get_cost>); break; case 'd': if (fn_name == "depth") return WRAP_FUNCTOR(get_wrapper<&get_depth>); else if (fn_name == "depth_parent") return WRAP_FUNCTOR(get_wrapper<&get_depth_parent>); else if (fn_name == "depth_spacer") return WRAP_FUNCTOR(get_wrapper<&get_depth_spacer>); break; case 'e': if (fn_name == "earliest") return WRAP_FUNCTOR(get_wrapper<&get_earliest>); else if (fn_name == "earliest_checkin") return WRAP_FUNCTOR(get_wrapper<&get_earliest_checkin>); break; case 'i': if (fn_name == "is_account") return WRAP_FUNCTOR(get_wrapper<&get_true>); else if (fn_name == "is_index") return WRAP_FUNCTOR(get_wrapper<&get_subcount>); break; case 'l': if (fn_name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_depth>); else if (fn_name == "latest_cleared") return WRAP_FUNCTOR(get_wrapper<&get_latest_cleared>); else if (fn_name == "latest") return WRAP_FUNCTOR(get_wrapper<&get_latest>); else if (fn_name == "latest_checkout") return WRAP_FUNCTOR(get_wrapper<&get_latest_checkout>); else if (fn_name == "latest_checkout_cleared") return WRAP_FUNCTOR(get_wrapper<&get_latest_checkout_cleared>); break; case 'n': if (fn_name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_subcount>); else if (fn_name == "note") return WRAP_FUNCTOR(get_wrapper<&get_note>); break; case 'p': if (fn_name == "partial_account") return WRAP_FUNCTOR(get_partial_name); else if (fn_name == "parent") return WRAP_FUNCTOR(get_wrapper<&get_parent>); break; case 's': if (fn_name == "subcount") return WRAP_FUNCTOR(get_wrapper<&get_subcount>); break; case 't': if (fn_name == "total") return WRAP_FUNCTOR(get_wrapper<&get_total>); break; case 'u': if (fn_name == "use_direct_amount") return WRAP_FUNCTOR(get_wrapper<&ignore>); break; case 'N': if (fn_name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_count>); break; case 'O': if (fn_name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_total>); break; } return NULL; } bool account_t::valid() const { if (depth > 256) { DEBUG("ledger.validate", "account_t: depth > 256"); return false; } foreach (const accounts_map::value_type& pair, accounts) { if (this == pair.second) { DEBUG("ledger.validate", "account_t: parent refers to itself!"); return false; } if (! pair.second->valid()) { DEBUG("ledger.validate", "account_t: child not valid"); return false; } } return true; } bool account_t::children_with_xdata() const { foreach (const accounts_map::value_type& pair, accounts) if (pair.second->has_xdata() || pair.second->children_with_xdata()) return true; return false; } std::size_t account_t::children_with_flags(xdata_t::flags_t flags) const { std::size_t count = 0; bool grandchildren_visited = false; foreach (const accounts_map::value_type& pair, accounts) if (pair.second->has_xflags(flags) || pair.second->children_with_flags(flags)) count++; // Although no immediately children were visited, if any progeny at all were // visited, it counts as one. if (count == 0 && grandchildren_visited) count = 1; return count; } account_t::xdata_t::details_t& account_t::xdata_t::details_t::operator+=(const details_t& other) { posts_count += other.posts_count; posts_virtuals_count += other.posts_virtuals_count; posts_cleared_count += other.posts_cleared_count; posts_last_7_count += other.posts_last_7_count; posts_last_30_count += other.posts_last_30_count; posts_this_month_count += other.posts_this_month_count; if (! is_valid(earliest_post) || (is_valid(other.earliest_post) && other.earliest_post < earliest_post)) earliest_post = other.earliest_post; if (! is_valid(earliest_cleared_post) || (is_valid(other.earliest_cleared_post) && other.earliest_cleared_post < earliest_cleared_post)) earliest_cleared_post = other.earliest_cleared_post; if (! is_valid(latest_post) || (is_valid(other.latest_post) && other.latest_post > latest_post)) latest_post = other.latest_post; if (! is_valid(latest_cleared_post) || (is_valid(other.latest_cleared_post) && other.latest_cleared_post > latest_cleared_post)) latest_cleared_post = other.latest_cleared_post; filenames.insert(other.filenames.begin(), other.filenames.end()); accounts_referenced.insert(other.accounts_referenced.begin(), other.accounts_referenced.end()); payees_referenced.insert(other.payees_referenced.begin(), other.payees_referenced.end()); return *this; } void account_t::clear_xdata() { xdata_ = none; foreach (accounts_map::value_type& pair, accounts) if (! pair.second->has_flags(ACCOUNT_TEMP)) pair.second->clear_xdata(); } value_t account_t::amount(const optional<bool> real_only, const optional<expr_t&>& expr) const { DEBUG("account.amount", "real only: " << real_only); if (xdata_ && xdata_->has_flags(ACCOUNT_EXT_VISITED)) { posts_list::const_iterator i; if (xdata_->self_details.last_post) i = *xdata_->self_details.last_post; else i = posts.begin(); for (; i != posts.end(); i++) { if ((*i)->xdata().has_flags(POST_EXT_VISITED)) { if (! (*i)->xdata().has_flags(POST_EXT_CONSIDERED)) { if (! (*i)->has_flags(POST_VIRTUAL)) { (*i)->add_to_value(xdata_->self_details.real_total, expr); } (*i)->add_to_value(xdata_->self_details.total, expr); (*i)->xdata().add_flags(POST_EXT_CONSIDERED); } } xdata_->self_details.last_post = i; } if (xdata_->self_details.last_reported_post) i = *xdata_->self_details.last_reported_post; else i = xdata_->reported_posts.begin(); for (; i != xdata_->reported_posts.end(); i++) { if ((*i)->xdata().has_flags(POST_EXT_VISITED)) { if (! (*i)->xdata().has_flags(POST_EXT_CONSIDERED)) { if (! (*i)->has_flags(POST_VIRTUAL)) { (*i)->add_to_value(xdata_->self_details.real_total, expr); } (*i)->add_to_value(xdata_->self_details.total, expr); (*i)->xdata().add_flags(POST_EXT_CONSIDERED); } } xdata_->self_details.last_reported_post = i; } if (real_only == true) { return xdata_->self_details.real_total; } else { return xdata_->self_details.total; } } else { return NULL_VALUE; } } value_t account_t::total(const optional<expr_t&>& expr) const { if (! (xdata_ && xdata_->family_details.calculated)) { const_cast<account_t&>(*this).xdata().family_details.calculated = true; value_t temp; foreach (const accounts_map::value_type& pair, accounts) { temp = pair.second->total(expr); if (! temp.is_null()) add_or_set_value(xdata_->family_details.total, temp); } temp = amount(false, expr); if (! temp.is_null()) add_or_set_value(xdata_->family_details.total, temp); } return xdata_->family_details.total; } const account_t::xdata_t::details_t& account_t::self_details(bool gather_all) const { if (! (xdata_ && xdata_->self_details.gathered)) { const_cast<account_t&>(*this).xdata().self_details.gathered = true; foreach (const post_t * post, posts) xdata_->self_details.update(const_cast<post_t&>(*post), gather_all); } return xdata_->self_details; } const account_t::xdata_t::details_t& account_t::family_details(bool gather_all) const { if (! (xdata_ && xdata_->family_details.gathered)) { const_cast<account_t&>(*this).xdata().family_details.gathered = true; foreach (const accounts_map::value_type& pair, accounts) xdata_->family_details += pair.second->family_details(gather_all); xdata_->family_details += self_details(gather_all); } return xdata_->family_details; } void account_t::xdata_t::details_t::update(post_t& post, bool gather_all) { posts_count++; if (post.has_flags(POST_VIRTUAL)) posts_virtuals_count++; if (gather_all && post.pos) filenames.insert(post.pos->pathname); date_t date = post.date(); if (date.year() == CURRENT_DATE().year() && date.month() == CURRENT_DATE().month()) posts_this_month_count++; if ((CURRENT_DATE() - date).days() <= 30) posts_last_30_count++; if ((CURRENT_DATE() - date).days() <= 7) posts_last_7_count++; if (! is_valid(earliest_post) || post.date() < earliest_post) earliest_post = post.date(); if (! is_valid(latest_post) || post.date() > latest_post) latest_post = post.date(); if (post.checkin && (earliest_checkin.is_not_a_date_time() || *post.checkin < earliest_checkin)) earliest_checkin = *post.checkin; if (post.checkout && (latest_checkout.is_not_a_date_time() || *post.checkout > latest_checkout)) { latest_checkout = *post.checkout; latest_checkout_cleared = post.state() == item_t::CLEARED; } if (post.state() == item_t::CLEARED) { posts_cleared_count++; if (! is_valid(earliest_cleared_post) || post.date() < earliest_cleared_post) earliest_cleared_post = post.date(); if (! is_valid(latest_cleared_post) || post.date() > latest_cleared_post) latest_cleared_post = post.date(); } if (gather_all) { accounts_referenced.insert(post.account->fullname()); payees_referenced.insert(post.payee()); } } void put_account(property_tree::ptree& st, const account_t& acct, function<bool(const account_t&)> pred) { if (pred(acct)) { std::ostringstream buf; buf.width(sizeof(intptr_t) * 2); buf.fill('0'); buf << std::hex << reinterpret_cast<intptr_t>(&acct); st.put("<xmlattr>.id", buf.str()); st.put("name", acct.name); st.put("fullname", acct.fullname()); value_t total = acct.amount(); if (! total.is_null()) put_value(st.put("account-amount", ""), total); total = acct.total(); if (! total.is_null()) put_value(st.put("account-total", ""), total); foreach (const accounts_map::value_type& pair, acct.accounts) put_account(st.add("account", ""), *pair.second, pred); } } } // namespace ledger �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/account.h��������������������������������������������������������������������������0000664�0000000�0000000�00000022452�14411236400�0015355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file account.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_ACCOUNT_H #define INCLUDED_ACCOUNT_H #include "scope.h" namespace ledger { class account_t; class xact_t; class post_t; typedef std::list<post_t *> posts_list; typedef std::map<string, account_t *> accounts_map; typedef std::map<string, posts_list> deferred_posts_map_t; class account_t : public supports_flags<>, public scope_t { #define ACCOUNT_NORMAL 0x00 // no flags at all, a basic account #define ACCOUNT_KNOWN 0x01 #define ACCOUNT_TEMP 0x02 // account is a temporary object #define ACCOUNT_GENERATED 0x04 // account never actually existed public: account_t * parent; string name; optional<string> note; unsigned short depth; accounts_map accounts; posts_list posts; optional<deferred_posts_map_t> deferred_posts; optional<expr_t> value_expr; mutable string _fullname; #if DOCUMENT_MODEL mutable void * data; #endif account_t(account_t * _parent = NULL, const string& _name = "", const optional<string>& _note = none) : supports_flags<>(), scope_t(), parent(_parent), name(_name), note(_note), depth(static_cast<unsigned short>(parent ? parent->depth + 1 : 0)) #if DOCUMENT_MODEL , data(NULL) #endif { TRACE_CTOR(account_t, "account_t *, const string&, const string&"); } account_t(const account_t& other) : supports_flags<>(other.flags()), scope_t(), parent(other.parent), name(other.name), note(other.note), depth(other.depth), accounts(other.accounts) #if DOCUMENT_MODEL , data(NULL) #endif { TRACE_CTOR(account_t, "copy"); } virtual ~account_t(); virtual string description() { return string(_("account ")) + fullname(); } operator string() const { return fullname(); } string fullname() const; string partial_name(bool flat = false) const; void add_account(account_t * acct) { accounts.insert(accounts_map::value_type(acct->name, acct)); } bool remove_account(account_t * acct) { accounts_map::size_type n = accounts.erase(acct->name); return n > 0; } account_t * find_account(const string& name, bool auto_create = true); account_t * find_account_re(const string& regexp); typedef transform_iterator<function<account_t *(accounts_map::value_type&)>, accounts_map::iterator> accounts_map_seconds_iterator; accounts_map_seconds_iterator accounts_begin() { return make_transform_iterator (accounts.begin(), boost::bind(&accounts_map::value_type::second, _1)); } accounts_map_seconds_iterator accounts_end() { return make_transform_iterator (accounts.end(), boost::bind(&accounts_map::value_type::second, _1)); } void add_post(post_t * post); void add_deferred_post(const string& uuid, post_t * post); void apply_deferred_posts(); bool remove_post(post_t * post); posts_list::iterator posts_begin() { return posts.begin(); } posts_list::iterator posts_end() { return posts.end(); } virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); bool valid() const; friend class journal_t; struct xdata_t : public supports_flags<> { #define ACCOUNT_EXT_SORT_CALC 0x01 #define ACCOUNT_EXT_HAS_NON_VIRTUALS 0x02 #define ACCOUNT_EXT_HAS_UNB_VIRTUALS 0x04 #define ACCOUNT_EXT_AUTO_VIRTUALIZE 0x08 #define ACCOUNT_EXT_VISITED 0x10 #define ACCOUNT_EXT_MATCHING 0x20 #define ACCOUNT_EXT_TO_DISPLAY 0x40 #define ACCOUNT_EXT_DISPLAYED 0x80 struct details_t { value_t total; value_t real_total; bool calculated; bool gathered; std::size_t posts_count; std::size_t posts_virtuals_count; std::size_t posts_cleared_count; std::size_t posts_last_7_count; std::size_t posts_last_30_count; std::size_t posts_this_month_count; date_t earliest_post; date_t earliest_cleared_post; date_t latest_post; date_t latest_cleared_post; datetime_t earliest_checkin; datetime_t latest_checkout; bool latest_checkout_cleared; std::set<path> filenames; std::set<string> accounts_referenced; std::set<string> payees_referenced; optional<posts_list::const_iterator> last_post; optional<posts_list::const_iterator> last_reported_post; details_t() : calculated(false), gathered(false), posts_count(0), posts_virtuals_count(0), posts_cleared_count(0), posts_last_7_count(0), posts_last_30_count(0), posts_this_month_count(0), latest_checkout_cleared(false) { TRACE_CTOR(account_t::xdata_t::details_t, ""); } // A copy copies nothing details_t(const details_t&) : calculated(false), gathered(false), posts_count(0), posts_virtuals_count(0), posts_cleared_count(0), posts_last_7_count(0), posts_last_30_count(0), posts_this_month_count(0), latest_checkout_cleared(false) { TRACE_CTOR(account_t::xdata_t::details_t, "copy"); } ~details_t() throw() { TRACE_DTOR(account_t::xdata_t::details_t); } details_t& operator+=(const details_t& other); void update(post_t& post, bool gather_all = false); }; details_t self_details; details_t family_details; posts_list reported_posts; std::list<sort_value_t> sort_values; xdata_t() : supports_flags<>() { TRACE_CTOR(account_t::xdata_t, ""); } xdata_t(const xdata_t& other) : supports_flags<>(other.flags()), self_details(other.self_details), family_details(other.family_details), sort_values(other.sort_values) { TRACE_CTOR(account_t::xdata_t, "copy"); } ~xdata_t() throw() { TRACE_DTOR(account_t::xdata_t); } }; // This variable holds optional "extended data" which is usually produced // only during reporting, and only for the posting set being reported. // It's a memory-saving measure to delay allocation until the last possible // moment. mutable optional<xdata_t> xdata_; bool has_xdata() const { return static_cast<bool>(xdata_); } void clear_xdata(); xdata_t& xdata() { if (! xdata_) xdata_ = xdata_t(); return *xdata_; } const xdata_t& xdata() const { assert(xdata_); return *xdata_; } value_t amount(const optional<bool> real_only = false, const optional<expr_t&>& expr = none) const; value_t total(const optional<expr_t&>& expr = none) const; const xdata_t::details_t& self_details(bool gather_all = true) const; const xdata_t::details_t& family_details(bool gather_all = true) const; bool has_xflags(xdata_t::flags_t flags) const { return xdata_ && xdata_->has_flags(flags); } bool children_with_xdata() const; std::size_t children_with_flags(xdata_t::flags_t flags) const; }; std::ostream& operator<<(std::ostream& out, const account_t& account); void put_account(property_tree::ptree& pt, const account_t& acct, function<bool(const account_t&)> pred); //simple struct added to allow std::map to compare accounts in the accounts report struct account_compare { bool operator() (const account_t& lhs, const account_t& rhs) const { return (lhs.fullname().compare(rhs.fullname()) < 0); } }; } // namespace ledger #endif // INCLUDED_ACCOUNT_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/amount.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000107541�14411236400�0015365�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include <math.h> #include "amount.h" #include "commodity.h" #include "annotate.h" #include "pool.h" namespace ledger { bool amount_t::stream_fullstrings = false; #if !defined(THREADSAFE) // These global temporaries are pre-initialized for the sake of // efficiency, and are reused over and over again. static mpz_t temp; static mpq_t tempq; static mpfr_t tempf; static mpfr_t tempfb; static mpfr_t tempfnum; static mpfr_t tempfden; #endif struct amount_t::bigint_t : public supports_flags<> { #define BIGINT_BULK_ALLOC 0x01 #define BIGINT_KEEP_PREC 0x02 mpq_t val; precision_t prec; uint_least32_t refc; #define MP(bigint) ((bigint)->val) bigint_t() : prec(0), refc(1) { mpq_init(val); TRACE_CTOR(bigint_t, ""); } bigint_t(const bigint_t& other) : supports_flags<>(static_cast<uint_least8_t> (other.flags() & ~BIGINT_BULK_ALLOC)), prec(other.prec), refc(1) { mpq_init(val); mpq_set(val, other.val); TRACE_CTOR(bigint_t, "copy"); } ~bigint_t() { TRACE_DTOR(bigint_t); assert(refc == 0); mpq_clear(val); } bool valid() const { if (prec > 1024) { DEBUG("ledger.validate", "amount_t::bigint_t: prec > 1024"); return false; } if (flags() & ~(BIGINT_BULK_ALLOC | BIGINT_KEEP_PREC)) { DEBUG("ledger.validate", "amount_t::bigint_t: flags() & ~(BULK_ALLOC | KEEP_PREC)"); return false; } return true; } }; bool amount_t::is_initialized = false; namespace { void stream_out_mpq(std::ostream& out, mpq_t quant, amount_t::precision_t precision, int zeros_prec = -1, mpfr_rnd_t rnd = GMP_RNDN, const optional<commodity_t&>& comm = none) { char * buf = NULL; try { #if DEBUG_ON IF_DEBUG("amount.convert") { char * tbuf = mpq_get_str(NULL, 10, quant); DEBUG("amount.convert", "Rational to convert = " << tbuf); std::free(tbuf); } #endif // Convert the rational number to a floating-point, extending the // floating-point to a large enough size to get a precise answer. mp_prec_t num_prec = static_cast<mpfr_prec_t>(mpz_sizeinbase(mpq_numref(quant), 2)); num_prec += amount_t::extend_by_digits*64; if (num_prec < MPFR_PREC_MIN) num_prec = MPFR_PREC_MIN; DEBUG("amount.convert", "num prec = " << num_prec); mpfr_set_prec(tempfnum, num_prec); mpfr_set_z(tempfnum, mpq_numref(quant), rnd); mp_prec_t den_prec = static_cast<mpfr_prec_t>(mpz_sizeinbase(mpq_denref(quant), 2)); den_prec += amount_t::extend_by_digits*64; if (den_prec < MPFR_PREC_MIN) den_prec = MPFR_PREC_MIN; DEBUG("amount.convert", "den prec = " << den_prec); mpfr_set_prec(tempfden, den_prec); mpfr_set_z(tempfden, mpq_denref(quant), rnd); mpfr_set_prec(tempfb, num_prec + den_prec); mpfr_div(tempfb, tempfnum, tempfden, rnd); if (mpfr_asprintf(&buf, "%.*RNf", precision, tempfb) < 0) throw_(amount_error, _("Cannot output amount to a floating-point representation")); DEBUG("amount.convert", "mpfr_print = " << buf << " (precision " << precision << ", zeros_prec " << zeros_prec << ")"); if (zeros_prec >= 0) { string::size_type index = std::strlen(buf); string::size_type point = 0; for (string::size_type i = 0; i < index; i++) { if (buf[i] == '.') { point = i; break; } } if (point > 0) { while (--index >= (point + 1 + static_cast<std::size_t>(zeros_prec)) && buf[index] == '0') buf[index] = '\0'; if (index >= (point + static_cast<std::size_t>(zeros_prec)) && buf[index] == '.') buf[index] = '\0'; } } if (comm) { int integer_digits = 0; if (comm && comm->has_flags(COMMODITY_STYLE_THOUSANDS)) { // Count the number of integer digits for (const char * p = buf; *p; p++) { if (*p == '.') break; else if (*p != '-') integer_digits++; } } for (const char * p = buf; *p; p++) { if (*p == '.') { if (("h" == comm->symbol() || "m" == comm->symbol()) && (commodity_t::time_colon_by_default || (comm && comm->has_flags(COMMODITY_STYLE_TIME_COLON)))) out << ':'; else if (commodity_t::decimal_comma_by_default || (comm && comm->has_flags(COMMODITY_STYLE_DECIMAL_COMMA))) out << ','; else out << *p; assert(integer_digits <= 3); } else if (*p == '-') { out << *p; } else { out << *p; if (integer_digits > 3 && --integer_digits % 3 == 0) { if (("h" == comm->symbol() || "m" == comm->symbol()) && (commodity_t::time_colon_by_default || (comm && comm->has_flags(COMMODITY_STYLE_TIME_COLON)))) out << ':'; else if (commodity_t::decimal_comma_by_default || (comm && comm->has_flags(COMMODITY_STYLE_DECIMAL_COMMA))) out << '.'; else out << ','; } } } } else { out << buf; } } catch (...) { if (buf != NULL) mpfr_free_str(buf); throw; } if (buf != NULL) mpfr_free_str(buf); } } void amount_t::initialize() { if (! is_initialized) { mpz_init(temp); mpq_init(tempq); mpfr_init(tempf); mpfr_init(tempfb); mpfr_init(tempfnum); mpfr_init(tempfden); commodity_pool_t::current_pool.reset(new commodity_pool_t); // Add time commodity conversions, so that timelogs may be parsed // in terms of seconds, but reported as minutes or hours. if (commodity_t * commodity = commodity_pool_t::current_pool->create("s")) commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); #if !NO_ASSERTS else assert(false); #endif // Add a "percentile" commodity if (commodity_t * commodity = commodity_pool_t::current_pool->create("%")) commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); #if !NO_ASSERTS else assert(false); #endif is_initialized = true; } } void amount_t::shutdown() { if (is_initialized) { mpz_clear(temp); mpq_clear(tempq); mpfr_clear(tempf); mpfr_clear(tempfb); mpfr_clear(tempfnum); mpfr_clear(tempfden); commodity_pool_t::current_pool.reset(); is_initialized = false; } } void amount_t::_copy(const amount_t& amt) { VERIFY(amt.valid()); if (quantity != amt.quantity) { if (quantity) _release(); // Never maintain a pointer into a bulk allocation pool; such // pointers are not guaranteed to remain. if (amt.quantity->has_flags(BIGINT_BULK_ALLOC)) { quantity = new bigint_t(*amt.quantity); } else { quantity = amt.quantity; DEBUG("amount.refs", quantity << " refc++, now " << (quantity->refc + 1)); quantity->refc++; } } commodity_ = amt.commodity_; VERIFY(valid()); } void amount_t::_dup() { VERIFY(valid()); if (quantity->refc > 1) { bigint_t * q = new bigint_t(*quantity); _release(); quantity = q; } VERIFY(valid()); } void amount_t::_clear() { if (quantity) { _release(); quantity = NULL; commodity_ = NULL; } else { assert(! commodity_); } } void amount_t::_release() { VERIFY(valid()); DEBUG("amount.refs", quantity << " refc--, now " << (quantity->refc - 1)); if (--quantity->refc == 0) { if (quantity->has_flags(BIGINT_BULK_ALLOC)) quantity->~bigint_t(); else checked_delete(quantity); quantity = NULL; commodity_ = NULL; } VERIFY(valid()); } amount_t::amount_t(const double val) : commodity_(NULL) { quantity = new bigint_t; mpq_set_d(MP(quantity), val); quantity->prec = extend_by_digits; // an approximation TRACE_CTOR(amount_t, "const double"); } amount_t::amount_t(const unsigned long val) : commodity_(NULL) { quantity = new bigint_t; mpq_set_ui(MP(quantity), val, 1); TRACE_CTOR(amount_t, "const unsigned long"); } amount_t::amount_t(const long val) : commodity_(NULL) { quantity = new bigint_t; mpq_set_si(MP(quantity), val, 1); TRACE_CTOR(amount_t, "const long"); } amount_t& amount_t::operator=(const amount_t& amt) { if (this != &amt) { if (amt.quantity) _copy(amt); else if (quantity) _clear(); } return *this; } int amount_t::compare(const amount_t& amt) const { VERIFY(amt.valid()); if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, _("Cannot compare an amount to an uninitialized amount")); else if (amt.quantity) throw_(amount_error, _("Cannot compare an uninitialized amount to an amount")); else throw_(amount_error, _("Cannot compare two uninitialized amounts")); } if (has_commodity() && amt.has_commodity() && commodity() != amt.commodity()) { throw_(amount_error, _f("Cannot compare amounts with different commodities: '%1%' and '%2%'") % commodity() % amt.commodity()); } return mpq_cmp(MP(quantity), MP(amt.quantity)); } bool amount_t::operator==(const amount_t& amt) const { if ((quantity && ! amt.quantity) || (! quantity && amt.quantity)) return false; else if (! quantity && ! amt.quantity) return true; else if (commodity() != amt.commodity()) return false; return mpq_equal(MP(quantity), MP(amt.quantity)); } amount_t& amount_t::operator+=(const amount_t& amt) { VERIFY(amt.valid()); if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, _("Cannot add an uninitialized amount to an amount")); else if (amt.quantity) throw_(amount_error, _("Cannot add an amount to an uninitialized amount")); else throw_(amount_error, _("Cannot add two uninitialized amounts")); } if (has_commodity() && amt.has_commodity() && commodity() != amt.commodity()) { throw_(amount_error, _f("Adding amounts with different commodities: '%1%' != '%2%'") % commodity() % amt.commodity()); } _dup(); mpq_add(MP(quantity), MP(quantity), MP(amt.quantity)); if (has_commodity() == amt.has_commodity()) if (quantity->prec < amt.quantity->prec) quantity->prec = amt.quantity->prec; return *this; } amount_t& amount_t::operator-=(const amount_t& amt) { VERIFY(amt.valid()); if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, _("Cannot subtract an amount from an uninitialized amount")); else if (amt.quantity) throw_(amount_error, _("Cannot subtract an uninitialized amount from an amount")); else throw_(amount_error, _("Cannot subtract two uninitialized amounts")); } if (has_commodity() && amt.has_commodity() && commodity() != amt.commodity()) { throw_(amount_error, _f("Subtracting amounts with different commodities: '%1%' != '%2%'") % commodity() % amt.commodity()); } _dup(); mpq_sub(MP(quantity), MP(quantity), MP(amt.quantity)); if (has_commodity() == amt.has_commodity()) if (quantity->prec < amt.quantity->prec) quantity->prec = amt.quantity->prec; return *this; } amount_t& amount_t::multiply(const amount_t& amt, bool ignore_commodity) { VERIFY(amt.valid()); if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, _("Cannot multiply an amount by an uninitialized amount")); else if (amt.quantity) throw_(amount_error, _("Cannot multiply an uninitialized amount by an amount")); else throw_(amount_error, _("Cannot multiply two uninitialized amounts")); } _dup(); mpq_mul(MP(quantity), MP(quantity), MP(amt.quantity)); quantity->prec = static_cast<precision_t>(quantity->prec + amt.quantity->prec); if (! has_commodity() && ! ignore_commodity) commodity_ = amt.commodity_; if (has_commodity() && ! keep_precision()) { precision_t comm_prec = commodity().precision(); if (quantity->prec > comm_prec + extend_by_digits) quantity->prec = static_cast<precision_t>(comm_prec + extend_by_digits); } return *this; } amount_t& amount_t::operator/=(const amount_t& amt) { VERIFY(amt.valid()); if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, _("Cannot divide an amount by an uninitialized amount")); else if (amt.quantity) throw_(amount_error, _("Cannot divide an uninitialized amount by an amount")); else throw_(amount_error, _("Cannot divide two uninitialized amounts")); } if (! amt) throw_(amount_error, _("Divide by zero")); _dup(); // Increase the value's precision, to capture fractional parts after // the divide. Round up in the last position. mpq_div(MP(quantity), MP(quantity), MP(amt.quantity)); quantity->prec = static_cast<precision_t>(quantity->prec + amt.quantity->prec + extend_by_digits); if (! has_commodity()) commodity_ = amt.commodity_; // If this amount has a commodity, and we're not dealing with plain // numbers, or internal numbers (which keep full precision at all // times), then round the number to within the commodity's precision // plus six places. if (has_commodity() && ! keep_precision()) { precision_t comm_prec = commodity().precision(); if (quantity->prec > comm_prec + extend_by_digits) quantity->prec = static_cast<precision_t>(comm_prec + extend_by_digits); } return *this; } amount_t::precision_t amount_t::precision() const { if (! quantity) throw_(amount_error, _("Cannot determine precision of an uninitialized amount")); return quantity->prec; } bool amount_t::keep_precision() const { if (! quantity) throw_(amount_error, _("Cannot determine if precision of an uninitialized amount is kept")); return quantity->has_flags(BIGINT_KEEP_PREC); } void amount_t::set_keep_precision(const bool keep) const { if (! quantity) throw_(amount_error, _("Cannot set whether to keep the precision of an uninitialized amount")); if (keep) quantity->add_flags(BIGINT_KEEP_PREC); else quantity->drop_flags(BIGINT_KEEP_PREC); } amount_t::precision_t amount_t::display_precision() const { if (! quantity) throw_(amount_error, _("Cannot determine display precision of an uninitialized amount")); commodity_t& comm(commodity()); if (comm && ! keep_precision()) return comm.precision(); else return comm ? std::max(quantity->prec, comm.precision()) : quantity->prec; } void amount_t::in_place_negate() { if (quantity) { _dup(); mpq_neg(MP(quantity), MP(quantity)); } else { throw_(amount_error, _("Cannot negate an uninitialized amount")); } } void amount_t::in_place_invert() { if (! quantity) throw_(amount_error, _("Cannot invert an uninitialized amount")); _dup(); if (sign() != 0) mpq_inv(MP(quantity), MP(quantity)); } void amount_t::in_place_round() { if (! quantity) throw_(amount_error, _("Cannot set rounding for an uninitialized amount")); else if (! keep_precision()) return; _dup(); set_keep_precision(false); } void amount_t::in_place_truncate() { #if 1 if (! quantity) throw_(amount_error, _("Cannot truncate an uninitialized amount")); _dup(); DEBUG("amount.truncate", "Truncating " << *this << " to precision " << display_precision()); std::ostringstream out; stream_out_mpq(out, MP(quantity), display_precision()); scoped_array<char> buf(new char [out.str().length() + 1]); std::strcpy(buf.get(), out.str().c_str()); char * q = buf.get(); for (char * p = q; *p != '\0'; p++, q++) { if (*p == '.') p++; if (p != q) *q = *p; } *q = '\0'; mpq_set_str(MP(quantity), buf.get(), 10); mpz_ui_pow_ui(temp, 10, display_precision()); mpq_set_z(tempq, temp); mpq_div(MP(quantity), MP(quantity), tempq); DEBUG("amount.truncate", "Truncated = " << *this); #else // This naive implementation is straightforward, but extremely inefficient // as it requires parsing the commodity too, which might be fully annotated. *this = amount_t(to_string()); #endif } void amount_t::in_place_floor() { if (! quantity) throw_(amount_error, _("Cannot compute floor on an uninitialized amount")); _dup(); mpz_fdiv_q(temp, mpq_numref(MP(quantity)), mpq_denref(MP(quantity))); mpq_set_z(MP(quantity), temp); } void amount_t::in_place_ceiling() { if (! quantity) throw_(amount_error, _("Cannot compute ceiling on an uninitialized amount")); _dup(); mpz_cdiv_q(temp, mpq_numref(MP(quantity)), mpq_denref(MP(quantity))); mpq_set_z(MP(quantity), temp); } void amount_t::in_place_roundto(int places) { if (! quantity) throw_(amount_error, _("Cannot round an uninitialized amount")); double x=ceil(mpq_get_d(MP(quantity))*pow(10, places) - 0.49999999) / pow(10, places); mpq_set_d(MP(quantity), x); } void amount_t::in_place_unround() { if (! quantity) throw_(amount_error, _("Cannot unround an uninitialized amount")); else if (keep_precision()) return; _dup(); DEBUG("amount.unround", "Unrounding " << *this); set_keep_precision(true); DEBUG("amount.unround", "Unrounded = " << *this); } void amount_t::in_place_reduce() { if (! quantity) throw_(amount_error, _("Cannot reduce an uninitialized amount")); while (commodity_ && commodity().smaller()) { *this *= commodity().smaller()->number(); commodity_ = commodity().smaller()->commodity_; } } void amount_t::in_place_unreduce() { if (! quantity) throw_(amount_error, _("Cannot unreduce an uninitialized amount")); amount_t tmp = *this; commodity_t * comm = commodity_; bool shifted = false; while (comm && comm->larger()) { amount_t next_temp = tmp / comm->larger()->number(); if (next_temp.abs() < amount_t(1L)) break; tmp = next_temp; comm = comm->larger()->commodity_; shifted = true; } if (shifted) { if (comm && ("h" == comm->symbol() || "m" == comm->symbol()) && commodity_t::time_colon_by_default) { double truncated = trunc(tmp.to_double()); double precision = tmp.to_double() - truncated; tmp = truncated + (precision * (comm->smaller()->number() / 100.0)); } *this = tmp; commodity_ = comm; } } optional<amount_t> amount_t::value(const datetime_t& moment, const commodity_t * in_terms_of) const { if (quantity) { #if DEBUG_ON DEBUG("commodity.price.find", "amount_t::value of " << commodity().symbol()); if (! moment.is_not_a_date_time()) DEBUG("commodity.price.find", "amount_t::value: moment = " << moment); if (in_terms_of) DEBUG("commodity.price.find", "amount_t::value: in_terms_of = " << in_terms_of->symbol()); #endif if (has_commodity() && (in_terms_of || ! commodity().has_flags(COMMODITY_PRIMARY))) { optional<price_point_t> point; const commodity_t * comm(in_terms_of); if (has_annotation() && annotation().price) { if (annotation().has_flags(ANNOTATION_PRICE_FIXATED)) { point = price_point_t(); point->price = *annotation().price; DEBUG("commodity.prices.find", "amount_t::value: fixated price = " << point->price); } else if (! comm) { comm = annotation().price->commodity_ptr(); } } if (comm && commodity().referent() == comm->referent()) return with_commodity(comm->referent()); if (! point) { point = commodity().find_price(comm, moment); // Whether a price was found or not, check whether we should attempt // to download a price from the Internet. This is done if (a) no // price was found, or (b) the price is "stale" according to the // setting of --price-exp. if (point) point = commodity().check_for_updated_price(point, moment, comm); } if (point) { amount_t price(point->price); price.multiply(*this, true); price.in_place_round(); return price; } } } else { throw_(amount_error, _("Cannot determine value of an uninitialized amount")); } return none; } optional<amount_t> amount_t::price() const { if (has_annotation() && annotation().price) { amount_t tmp(*annotation().price); tmp *= *this; DEBUG("amount.price", "Returning price of " << *this << " = " << tmp); return tmp; } return none; } int amount_t::sign() const { if (! quantity) throw_(amount_error, _("Cannot determine sign of an uninitialized amount")); return mpq_sgn(MP(quantity)); } bool amount_t::is_zero() const { if (! quantity) throw_(amount_error, _("Cannot determine if an uninitialized amount is zero")); if (has_commodity()) { if (keep_precision() || quantity->prec <= commodity().precision()) { return is_realzero(); } else if (is_realzero()) { return true; } else if (mpz_cmp(mpq_numref(MP(quantity)), mpq_denref(MP(quantity))) > 0) { DEBUG("amount.is_zero", "Numerator is larger than the denominator"); return false; } else { DEBUG("amount.is_zero", "We have to print the number to check for zero"); std::ostringstream out; stream_out_mpq(out, MP(quantity), commodity().precision()); string output = out.str(); if (! output.empty()) { for (const char * p = output.c_str(); *p; p++) if (*p != '0' && *p != '.' && *p != '-') return false; } return true; } } return is_realzero(); } double amount_t::to_double() const { if (! quantity) throw_(amount_error, _("Cannot convert an uninitialized amount to a double")); mpfr_set_q(tempf, MP(quantity), GMP_RNDN); return mpfr_get_d(tempf, GMP_RNDN); } long amount_t::to_long() const { if (! quantity) throw_(amount_error, _("Cannot convert an uninitialized amount to a long")); mpfr_set_q(tempf, MP(quantity), GMP_RNDN); return mpfr_get_si(tempf, GMP_RNDN); } bool amount_t::fits_in_long() const { mpfr_set_q(tempf, MP(quantity), GMP_RNDN); return mpfr_fits_slong_p(tempf, GMP_RNDN); } commodity_t * amount_t::commodity_ptr() const { return (commodity_ ? commodity_ : commodity_pool_t::current_pool->null_commodity); } bool amount_t::has_commodity() const { return commodity_ && commodity_ != commodity_->pool().null_commodity; } void amount_t::annotate(const annotation_t& details) { commodity_t * this_base; annotated_commodity_t * this_ann = NULL; if (! quantity) throw_(amount_error, _("Cannot annotate the commodity of an uninitialized amount")); else if (! has_commodity()) return; // ignore attempt to annotate a "bare commodity if (commodity().has_annotation()) { this_ann = &as_annotated_commodity(commodity()); this_base = &this_ann->referent(); } else { this_base = &commodity(); } assert(this_base); DEBUG("amount.commodities", "Annotating commodity for amount " << *this << std::endl << details); if (commodity_t * ann_comm = this_base->pool().find_or_create(*this_base, details)) set_commodity(*ann_comm); #if !NO_ASSERTS else assert(false); #endif DEBUG("amount.commodities", "Annotated amount is " << *this); } bool amount_t::has_annotation() const { if (! quantity) throw_(amount_error, _("Cannot determine if an uninitialized amount's commodity is annotated")); assert(! has_commodity() || ! commodity().has_annotation() || as_annotated_commodity(commodity()).details); return has_commodity() && commodity().has_annotation(); } annotation_t& amount_t::annotation() { if (! quantity) throw_(amount_error, _("Cannot return commodity annotation details of an uninitialized amount")); if (! commodity().has_annotation()) throw_(amount_error, _("Request for annotation details from an unannotated amount")); annotated_commodity_t& ann_comm(as_annotated_commodity(commodity())); return ann_comm.details; } amount_t amount_t::strip_annotations(const keep_details_t& what_to_keep) const { if (! quantity) throw_(amount_error, _("Cannot strip commodity annotations from an uninitialized amount")); if (! what_to_keep.keep_all(commodity())) { amount_t t(*this); t.set_commodity(commodity().strip_annotations(what_to_keep)); return t; } return *this; } namespace { void parse_quantity(std::istream& in, string& value) { char buf[256]; int c = peek_next_nonws(in); int max = 255; char *p = buf; if (c == '-') { *p++ = c; max--; in.get(); } READ_INTO(in, p, max, c, std::isdigit(c) || c == '.' || c == ','); string::size_type len = std::strlen(buf); while (len > 0 && ! std::isdigit(buf[len - 1])) { buf[--len] = '\0'; in.unget(); } value = buf; } } bool amount_t::parse(std::istream& in, const parse_flags_t& flags) { // The possible syntax for an amount is: // // [-]NUM[ ]SYM [@ AMOUNT] // SYM[ ][-]NUM [@ AMOUNT] string symbol; string quant; annotation_t details; bool negative = false; commodity_t::flags_t comm_flags = COMMODITY_STYLE_DEFAULTS; int c = peek_next_nonws(in); if (c == '-') { negative = true; in.get(); c = peek_next_nonws(in); } char n; if (std::isdigit(c)) { parse_quantity(in, quant); if (! in.eof() && ((n = static_cast<char>(in.peek())) != '\n')) { if (std::isspace(n)) comm_flags |= COMMODITY_STYLE_SEPARATED; commodity_t::parse_symbol(in, symbol); if (! symbol.empty()) comm_flags |= COMMODITY_STYLE_SUFFIXED; if (! flags.has_flags(PARSE_NO_ANNOT) && ! in.eof() && ((n = static_cast<char>(in.peek())) != '\n')) details.parse(in); } } else { commodity_t::parse_symbol(in, symbol); if (! in.eof() && ((n = static_cast<char>(in.peek())) != '\n')) { if (std::isspace(static_cast<char>(in.peek()))) comm_flags |= COMMODITY_STYLE_SEPARATED; parse_quantity(in, quant); if (! flags.has_flags(PARSE_NO_ANNOT) && ! quant.empty() && ! in.eof() && ((n = static_cast<char>(in.peek())) != '\n')) details.parse(in); } } if (quant.empty()) { if (flags.has_flags(PARSE_SOFT_FAIL)) return false; else throw_(amount_error, _("No quantity specified for amount")); } // Allocate memory for the amount's quantity value. We have to // monitor the allocation in a unique_ptr because this function gets // called sometimes from amount_t's constructor; and if there is an // exception thrown by any of the function calls after this point, // the destructor will never be called and the memory never freed. unique_ptr<bigint_t> new_quantity; if (quantity) { if (quantity->refc > 1) _release(); else new_quantity.reset(quantity); quantity = NULL; } if (! new_quantity.get()) new_quantity.reset(new bigint_t); // No one is holding a reference to this now. new_quantity->refc--; // Create the commodity if has not already been seen, and update the // precision if something greater was used for the quantity. if (symbol.empty()) { commodity_ = NULL; } else { commodity_ = commodity_pool_t::current_pool->find(symbol); if (! commodity_) commodity_ = commodity_pool_t::current_pool->create(symbol); assert(commodity_); } // Quickly scan through and verify the correctness of the amount's use of // punctuation. precision_t decimal_offset = 0; string::size_type string_index = quant.length(); string::size_type last_comma = string::npos; string::size_type last_period = string::npos; bool no_more_commas = false; bool no_more_periods = false; bool no_migrate_style = commodity().has_flags(COMMODITY_STYLE_NO_MIGRATE); bool decimal_comma_style = (commodity_t::decimal_comma_by_default || commodity().has_flags(COMMODITY_STYLE_DECIMAL_COMMA)); #if 0 bool time_colon_style = (commodity_t::time_colon_by_default || commodity().has_flags(COMMODITY_STYLE_TIME_COLON)); #endif new_quantity->prec = 0; BOOST_REVERSE_FOREACH (const char& ch, quant) { string_index--; if (ch == '.') { if (no_more_periods) throw_(amount_error, _("Too many periods in amount")); if (decimal_comma_style) { if (decimal_offset % 3 != 0) throw_(amount_error, _("Incorrect use of thousand-mark period")); comm_flags |= COMMODITY_STYLE_THOUSANDS; no_more_commas = true; } else { if (last_comma != string::npos) { decimal_comma_style = true; if (decimal_offset % 3 != 0) throw_(amount_error, _("Incorrect use of thousand-mark period")); } else { no_more_periods = true; new_quantity->prec = decimal_offset; decimal_offset = 0; } } if (last_period == string::npos) last_period = string_index; } else if (ch == ',') { if (no_more_commas) throw_(amount_error, _("Too many commas in amount")); if (decimal_comma_style) { if (last_period != string::npos) { throw_(amount_error, _("Incorrect use of decimal comma")); } else { no_more_commas = true; new_quantity->prec = decimal_offset; decimal_offset = 0; } } else { if (decimal_offset % 3 != 0) { if (last_comma != string::npos || last_period != string::npos) { throw_(amount_error, _("Incorrect use of thousand-mark comma")); } else { decimal_comma_style = true; no_more_commas = true; new_quantity->prec = decimal_offset; decimal_offset = 0; } } else { comm_flags |= COMMODITY_STYLE_THOUSANDS; no_more_periods = true; } } if (last_comma == string::npos) last_comma = string_index; } else { decimal_offset++; } } if (decimal_comma_style) comm_flags |= COMMODITY_STYLE_DECIMAL_COMMA; if (flags.has_flags(PARSE_NO_MIGRATE)) { // Can't call set_keep_precision here, because it assumes that `quantity' // is non-NULL. new_quantity->add_flags(BIGINT_KEEP_PREC); } else if (commodity_ && ! no_migrate_style) { commodity().add_flags(comm_flags); if (new_quantity->prec > commodity().precision()) commodity().set_precision(new_quantity->prec); } // Now we have the final number. Remove commas and periods, if necessary. if (last_comma != string::npos || last_period != string::npos) { string::size_type len = quant.length(); scoped_array<char> buf(new char[len + 1]); const char * p = quant.c_str(); char * t = buf.get(); while (*p) { if (*p == ',' || *p == '.') p++; *t++ = *p++; } *t = '\0'; mpq_set_str(MP(new_quantity.get()), buf.get(), 10); mpz_ui_pow_ui(temp, 10, new_quantity->prec); mpq_set_z(tempq, temp); mpq_div(MP(new_quantity.get()), MP(new_quantity.get()), tempq); IF_DEBUG("amount.parse") { char * amt_buf = mpq_get_str(NULL, 10, MP(new_quantity.get())); DEBUG("amount.parse", "Rational parsed = " << amt_buf); std::free(amt_buf); } } else { mpq_set_str(MP(new_quantity.get()), quant.c_str(), 10); } if (negative) mpq_neg(MP(new_quantity.get()), MP(new_quantity.get())); new_quantity->refc++; quantity = new_quantity.release(); if (! flags.has_flags(PARSE_NO_REDUCE)) in_place_reduce(); // will not throw an exception if (commodity_ && details) { if (details.has_flags(ANNOTATION_PRICE_NOT_PER_UNIT)) { assert(details.price); *details.price /= this->abs(); } set_commodity(*commodity_pool_t::current_pool->find_or_create(*commodity_, details)); } VERIFY(valid()); return true; } void amount_t::parse_conversion(const string& larger_str, const string& smaller_str) { amount_t larger, smaller; larger.parse(larger_str, PARSE_NO_REDUCE); smaller.parse(smaller_str, PARSE_NO_REDUCE); larger *= smaller.number(); if (larger.commodity()) { larger.commodity().set_smaller(smaller); larger.commodity().add_flags(smaller.commodity().flags() | COMMODITY_NOMARKET); } if (smaller.commodity()) smaller.commodity().set_larger(larger); } void amount_t::print(std::ostream& _out, const uint_least8_t flags) const { VERIFY(valid()); if (! quantity) { _out << "<null>"; return; } std::ostringstream out; commodity_t& comm(commodity()); if (! comm.has_flags(COMMODITY_STYLE_SUFFIXED)) { comm.print(out, flags & AMOUNT_PRINT_ELIDE_COMMODITY_QUOTES); if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) out << " "; } stream_out_mpq(out, MP(quantity), display_precision(), comm ? commodity().precision() : 0, GMP_RNDN, comm); if (comm.has_flags(COMMODITY_STYLE_SUFFIXED)) { if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) out << " "; comm.print(out, flags & AMOUNT_PRINT_ELIDE_COMMODITY_QUOTES); } // If there are any annotations associated with this commodity, output them // now. comm.write_annotations(out, flags & AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS); // Things are output to a string first, so that if anyone has specified a // width or fill for _out, it will be applied to the entire amount string, // and not just the first part. _out << out.str(); } bool amount_t::valid() const { if (quantity) { if (! quantity->valid()) { DEBUG("ledger.validate", "amount_t: ! quantity->valid()"); return false; } if (quantity->refc == 0) { DEBUG("ledger.validate", "amount_t: quantity->refc == 0"); return false; } } else if (commodity_) { DEBUG("ledger.validate", "amount_t: commodity_ != NULL"); return false; } return true; } void put_amount(property_tree::ptree& st, const amount_t& amt, bool commodity_details) { if (amt.has_commodity()) put_commodity(st.put("commodity", ""), amt.commodity(), commodity_details); st.put("quantity", amt.quantity_string()); } } // namespace ledger ���������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/amount.h���������������������������������������������������������������������������0000664�0000000�0000000�00000061710�14411236400�0015224�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @defgroup math Mathematical objects */ /** * @file amount.h * @author John Wiegley * * @ingroup math * * @brief Basic type for handling commoditized math: amount_t * * An amount is the most basic numerical type in Ledger, and relies on * commodity.h to represent commoditized amounts, which allows Ledger to * handle mathematical expressions involving disparate commodities. * * Amounts can be of virtually infinite size and precision. When * division or multiplication is performed, the precision is * automatically expanded to include as many extra digits as necessary * to avoid losing information. */ #ifndef INCLUDED_AMOUNT_H #define INCLUDED_AMOUNT_H #include "utils.h" #include "times.h" #include "flags.h" namespace ledger { class commodity_t; struct annotation_t; struct keep_details_t; DECLARE_EXCEPTION(amount_error, std::runtime_error); enum parse_flags_enum_t { PARSE_DEFAULT = 0x00, PARSE_PARTIAL = 0x01, PARSE_SINGLE = 0x02, PARSE_NO_MIGRATE = 0x04, PARSE_NO_REDUCE = 0x08, PARSE_NO_ASSIGN = 0x10, PARSE_NO_ANNOT = 0x20, PARSE_OP_CONTEXT = 0x40, PARSE_SOFT_FAIL = 0x80 }; typedef basic_flags_t<parse_flags_enum_t, uint_least8_t> parse_flags_t; /** * @brief Encapsulate infinite-precision commoditized amounts * * Used to represent commoditized infinite-precision numbers, and * uncommoditized, plain numbers. In the commoditized case, commodities * keep track of how they are used, and are always displayed back to the * user after the same fashion. For uncommoditized numbers, no display * truncation is ever done. In both cases, internal precision is always * kept to an excessive degree. */ class amount_t : public ordered_field_operators<amount_t, ordered_field_operators<amount_t, double, ordered_field_operators<amount_t, unsigned long, ordered_field_operators<amount_t, long> > > > { public: /** Ready the amount subsystem for use. @note Normally called by session_t::initialize(). */ static void initialize(); /** Shutdown the amount subsystem and free all resources. @note Normally called by session_t::shutdown(). */ static void shutdown(); static bool is_initialized; /** The amount's decimal precision. */ typedef uint_least16_t precision_t; /** Number of places of precision by which values are extended to avoid losing precision during division and multiplication. */ static const std::size_t extend_by_digits = 6U; /** If amounts should be streamed using to_fullstring() rather than to_string(), so that complete precision is always displayed no matter what the precision of an individual commodity may be. */ static bool stream_fullstrings; protected: void _copy(const amount_t& amt); void _dup(); void _clear(); void _release(); struct bigint_t; bigint_t * quantity; commodity_t * commodity_; public: /** @name Constructors @{ */ /** Creates a value for which is_null() is true, and which has no value or commodity. If used in a value expression it evaluates to zero, and its commodity equals \c commodity_t::null_commodity. */ amount_t() : quantity(NULL), commodity_(NULL) { TRACE_CTOR(amount_t, ""); } /** Convert a double to an amount. As much precision as possible is decoded from the binary floating point number. */ amount_t(const double val); /** Convert an unsigned long to an amount. It's precision is zero. */ amount_t(const unsigned long val); /** Convert a long to an amount. It's precision is zero, and the sign is preserved. */ amount_t(const long val); /** Parse a string as an (optionally commoditized) amount. If no commodity is present, the resulting commodity is \c commodity_t::null_commodity. The number may be of infinite precision. */ explicit amount_t(const string& val) : quantity(NULL) { parse(val); TRACE_CTOR(amount_t, "const string&"); } /** Parse a pointer to a C string as an (optionally commoditized) amount. If no commodity is present, the resulting commodity is \c commodity_t::null_commodity. The number may be of infinite precision. */ explicit amount_t(const char * val) : quantity(NULL) { assert(val); parse(val); TRACE_CTOR(amount_t, "const char *"); } /*@}*/ /** Create an amount whose display precision is never truncated, even if the amount uses a commodity (which normally causes "round on streaming" to occur). This function is mostly used by debugging code and unit tests. This is the proper way to specify \c $100.005, where display of the extra digit precision is required. If a regular constructor were used, the amount would stream as \c $100.01, even though its internal value equals \c $100.005. */ static amount_t exact(const string& value); /** Release the reference count held for the underlying \c amount_t::bigint_t object. */ ~amount_t() { TRACE_DTOR(amount_t); if (quantity) _release(); } /** @name Assignment and copy @{*/ /** Copy an amount object. Copies are very efficient, using a copy-on-write model. Until the copy is changed, it refers to the same memory used by the original via reference counting. The \c amount_t::bigint_t class in amount.cc maintains the reference. */ amount_t(const amount_t& amt) : quantity(NULL) { if (amt.quantity) _copy(amt); else commodity_ = NULL; TRACE_CTOR(amount_t, "copy"); } /** Copy an amount object, applying the given commodity annotation details afterward. This is equivalent to doing a normal copy (@see amount_t(const amount_t&)) and then calling amount_t::annotate(). */ amount_t(const amount_t& amt, const annotation_t& details) : quantity(NULL) { assert(amt.quantity); _copy(amt); annotate(details); TRACE_CTOR(amount_t, "const amount_t&, const annotation_t&"); } /** Assign an amount object. This is like copying if the amount was null beforehand, otherwise the previous value's reference is must be freed. */ amount_t& operator=(const amount_t& amt); amount_t& operator=(const double val) { return *this = amount_t(val); } amount_t& operator=(const unsigned long val) { return *this = amount_t(val); } amount_t& operator=(const long val) { return *this = amount_t(val); } /* Assign a string to an amount. This causes the contents of the string to be parsed, look for a commoditized or uncommoditized amount specifier. */ amount_t& operator=(const string& str) { return *this = amount_t(str); } amount_t& operator=(const char * str) { assert(str); return *this = amount_t(str); } /*@}*/ /** @name Comparison @{ */ /** Compare two amounts, returning a number less than zero if \p amt is greater, exactly zero if they are equal, and greater than zero if \p amt is less. This method is used to implement all of the other comparison methods.*/ int compare(const amount_t& amt) const; /** Test two amounts for equality. First the commodity pointers are quickly tested, then the multi-precision values themselves must be compared. */ bool operator==(const amount_t& amt) const; template <typename T> bool operator==(const T& val) const { return compare(val) == 0; } template <typename T> bool operator<(const T& amt) const { return compare(amt) < 0; } template <typename T> bool operator>(const T& amt) const { return compare(amt) > 0; } /*@}*/ /** @name Binary arithmetic */ /*@{*/ amount_t& operator+=(const amount_t& amt); amount_t& operator-=(const amount_t& amt); amount_t& operator*=(const amount_t& amt) { return multiply(amt); } amount_t& multiply(const amount_t& amt, bool ignore_commodity = false); /** Divide two amounts while extending the precision to preserve the accuracy of the result. For example, if \c 10 is divided by \c 3, the result ends up having a precision of \link amount_t::extend_by_digits \endlink place to avoid losing internal resolution. */ amount_t& operator/=(const amount_t& amt); /*@}*/ /** @name Unary arithmetic @{ */ /** Return an amount's internal precision. To find the precision it should be displayed at -- assuming it was not created using amount_t::exact() -- use the following expression instead: @code amount.commodity().precision() @endcode */ precision_t precision() const; bool keep_precision() const; void set_keep_precision(const bool keep = true) const; precision_t display_precision() const; /** Returns the negated value of an amount. @see operator-() */ amount_t negated() const { amount_t temp(*this); temp.in_place_negate(); return temp; } void in_place_negate(); amount_t operator-() const { return negated(); } /** Returns the absolute value of an amount. Equivalent to: @code (x < * 0) ? - x : x @endcode */ amount_t abs() const { if (sign() < 0) return negated(); return *this; } amount_t inverted() const { amount_t temp(*this); temp.in_place_invert(); return temp; } void in_place_invert(); /** Yields an amount whose display precision when output is truncated to the display precision of its commodity. This is normally the default state of an amount, but if one has become unrounded, this sets the "keep precision" state back to false. @see set_keep_precision */ amount_t rounded() const { amount_t temp(*this); temp.in_place_round(); return temp; } void in_place_round(); amount_t roundto(int places) const { amount_t temp(*this); temp.in_place_round(); return temp; } void in_place_roundto(int places); /** Yields an amount which has lost all of its extra precision, beyond what the display precision of the commodity would have printed. */ amount_t truncated() const { amount_t temp(*this); temp.in_place_truncate(); return temp; } void in_place_truncate(); /** Yields an amount which has lost all of its extra precision, beyond what the display precision of the commodity would have printed. */ amount_t floored() const { amount_t temp(*this); temp.in_place_floor(); return temp; } void in_place_floor(); /** Yields an amount which has lost all of its extra precision, beyond what the display precision of the commodity would have printed. */ amount_t ceilinged() const { amount_t temp(*this); temp.in_place_ceiling(); return temp; } void in_place_ceiling(); /** Yields an amount whose display precision is never truncated, even though its commodity normally displays only rounded values. */ amount_t unrounded() const { amount_t temp(*this); temp.in_place_unround(); return temp; } void in_place_unround(); /** reduces a value to its most basic commodity form, for amounts that utilize "scaling commodities". For example, an amount of \c 1h after reduction will be \c 3600s. */ amount_t reduced() const { amount_t temp(*this); temp.in_place_reduce(); return temp; } void in_place_reduce(); /** unreduce(), if used with a "scaling commodity", yields the most compact form greater than one. That is, \c 3599s will unreduce to \c 59.98m, while \c 3601 unreduces to \c 1h. */ amount_t unreduced() const { amount_t temp(*this); temp.in_place_unreduce(); return temp; } void in_place_unreduce(); /** Returns the historical value for an amount -- the default moment returns the most recently known price -- based on the price history for the given commodity (or determined automatically, if none is provided). For example, if the amount were <tt>10 AAPL</tt>, and on Apr 10, 2000 each share of \c AAPL was worth \c $10, then calling value() for that moment in time would yield the amount \c $100.00. */ optional<amount_t> value(const datetime_t& moment = datetime_t(), const commodity_t * in_terms_of = NULL) const; optional<amount_t> price() const; /*@}*/ /** @name Truth tests */ /*@{*/ /** Truth tests. An amount may be truth test in several ways: sign() returns an integer less than, greater than, or equal to zero depending on whether the amount is negative, zero, or greater than zero. Note that this function tests the actual value of the amount -- using its internal precision -- and not the display value. To test its display value, use: `round().sign()'. is_nonzero(), or operator bool, returns true if an amount's display value is not zero. is_zero() returns true if an amount's display value is zero. Thus, $0.0001 is considered zero if the current display precision for dollars is two decimal places. is_realzero() returns true if an amount's actual value is zero. Thus, $0.0001 is never considered realzero. is_null() returns true if an amount has no value and no commodity. This only occurs if an uninitialized amount has never been assigned a value. */ int sign() const; operator bool() const { return is_nonzero(); } bool is_nonzero() const { return ! is_zero(); } bool is_zero() const; bool is_realzero() const { return sign() == 0; } bool is_null() const { if (! quantity) { assert(! commodity_); return true; } return false; } /*@}*/ /** @name Conversion */ /*@{*/ /** Conversion methods. An amount may be converted to the same types it can be constructed from -- with the exception of unsigned long. Implicit conversions are not allowed in C++ (though they are in Python), rather the following conversion methods must be called explicitly: to_double([bool]) returns an amount as a double. If the optional boolean argument is true (the default), an exception is thrown if the conversion would lose information. to_long([bool]) returns an amount as a long integer. If the optional boolean argument is true (the default), an exception is thrown if the conversion would lose information. fits_in_long() returns true if to_long() would not lose precision. to_string() returns an amount's "display value" as a string -- after rounding the value according to the commodity's default precision. It is equivalent to: `round().to_fullstring()'. to_fullstring() returns an amount's "internal value" as a string, without any rounding. quantity_string() returns an amount's "display value", but without any commodity. Note that this is different from `number().to_string()', because in that case the commodity has been stripped and the full, internal precision of the amount would be displayed. */ double to_double() const; long to_long() const; bool fits_in_long() const; operator string() const { return to_string(); } string to_string() const; string to_fullstring() const; string quantity_string() const; /*@}*/ /** @name Commodity methods */ /*@{*/ /** The following methods relate to an amount's commodity: commodity() returns an amount's commodity. If the amount has no commodity, the value returned is the `null_commodity'. has_commodity() returns true if the amount has a commodity. set_commodity(commodity_t) sets an amount's commodity to the given value. Note that this merely sets the current amount to that commodity, it does not "observe" the amount for possible changes in the maximum display precision of the commodity, the way that `parse' does. clear_commodity() sets an amount's commodity to null, such that has_commodity() afterwards returns false. number() returns a commodity-less version of an amount. This is useful for accessing just the numeric portion of an amount. */ commodity_t * commodity_ptr() const; commodity_t& commodity() const { return *commodity_ptr(); } bool has_commodity() const; void set_commodity(commodity_t& comm) { if (! quantity) *this = 0L; commodity_ = &comm; } amount_t with_commodity(const commodity_t& comm) const { if (commodity_ == &comm) { return *this; } else { amount_t tmp(*this); tmp.set_commodity(const_cast<commodity_t&>(comm)); return tmp; } } void clear_commodity() { commodity_ = NULL; } amount_t number() const { if (! has_commodity()) return *this; amount_t temp(*this); temp.clear_commodity(); return temp; } /*@}*/ /** @name Commodity annotations */ /*@{*/ /** An amount's commodity may be annotated with special details, such as the price it was purchased for, when it was acquired, or an arbitrary note, identifying perhaps the lot number of an item. annotate_commodity(amount_t price, [datetime_t date, string tag]) sets the annotations for the current amount's commodity. Only the price argument is required, although it can be passed as `none' if no price is desired. commodity_annotated() returns true if an amount's commodity has any annotation details associated with it. annotation_details() returns all of the details of an annotated commodity's annotations. The structure returns will evaluate as boolean false if there are no details. strip_annotations() returns an amount whose commodity's annotations have been stripped. */ void annotate(const annotation_t& details); bool has_annotation() const; annotation_t& annotation(); const annotation_t& annotation() const { return const_cast<amount_t&>(*this).annotation(); } /** If the lot price is considered whenever working with commoditized values. Let's say a user adds two values of the following form: @code 10 AAPL + 10 AAPL {$20} @endcode This expression adds ten shares of Apple stock with another ten shares that were purchased for \c $20 a share. If \c keep_price is false, the result of this expression is an amount equal to <tt>20 AAPL</tt>. If \c keep_price is \c true the expression yields an exception for adding amounts with different commodities. In that case, a \link balance_t \endlink object must be used to store the combined sum. */ amount_t strip_annotations(const keep_details_t& what_to_keep) const; /*@}*/ /** @name Parsing */ /*@{*/ /** The `flags' argument of both parsing may be one or more of the following: PARSE_NO_MIGRATE means to not pay attention to the way an amount is used. Ordinarily, if an amount were $100.001, for example, it would cause the default display precision for $ to be "widened" to three decimal places. If PARSE_NO_MIGRATE is used, the commodity's default display precision is not changed. PARSE_NO_REDUCE means not to call in_place_reduce() on the resulting amount after it is parsed. These parsing methods observe the amounts they parse (unless PARSE_NO_MIGRATE is true), and set the display details of the corresponding commodity accordingly. This way, amounts do not require commodities to be pre-defined in any way, but merely displays them back to the user in the same fashion as it saw them used. There is also a static convenience method called `parse_conversion' which can be used to define a relationship between scaling commodity values. For example, Ledger uses it to define the relationships among various time values: @code amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes @endcode The method parse() is used to parse an amount from an input stream or a string. A global operator>>() is also defined which simply calls parse on the input stream. The parse() method has two forms: parse(istream, flags_t) parses an amount from the given input stream. parse(string, flags_t) parses an amount from the given string. parse(string, flags_t) also parses an amount from a string. */ bool parse(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT); bool parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) { std::istringstream stream(str); bool result = parse(stream, flags); return result; } static void parse_conversion(const string& larger_str, const string& smaller_str); /*@}*/ /** @name Printing */ /*@{*/ /** An amount may be output to a stream using the `print' method. There is also a global operator<< defined which simply calls print for an amount on the given stream. There is one form of the print method, which takes one required argument and two arguments with default values: print(ostream, bool omit_commodity = false, bool full_precision = false) prints an amounts to the given output stream, using its commodity's default display characteristics. If `omit_commodity' is true, the commodity will not be displayed, only the amount (although the commodity's display precision is still used). If `full_precision' is true, the full internal precision of the amount is displayed, regardless of its commodity's display precision. */ #define AMOUNT_PRINT_NO_FLAGS 0x00 #define AMOUNT_PRINT_RIGHT_JUSTIFY 0x01 #define AMOUNT_PRINT_COLORIZE 0x02 #define AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS 0x04 #define AMOUNT_PRINT_ELIDE_COMMODITY_QUOTES 0x08 void print(std::ostream& out, const uint_least8_t flags = AMOUNT_PRINT_NO_FLAGS) const; /*@}*/ /** @name Debugging */ /*@{*/ /** There are two methods defined to help with debugging: dump(ostream) dumps an amount to an output stream. There is little different from print(), it simply surrounds the display value with a marker, for example "AMOUNT($1.00)". This code is used by other dumping code elsewhere in Ledger. valid() returns true if an amount is valid. This ensures that if an amount has a commodity, it has a valid value pointer, for example, even if that pointer simply points to a zero value. */ void dump(std::ostream& out) const { out << "AMOUNT("; print(out); out << ")"; } bool valid() const; /*@}*/ }; inline amount_t amount_t::exact(const string& value) { amount_t temp; temp.parse(value, PARSE_NO_MIGRATE); return temp; } inline string amount_t::to_string() const { std::ostringstream bufstream; print(bufstream); return bufstream.str(); } inline string amount_t::to_fullstring() const { std::ostringstream bufstream; unrounded().print(bufstream); return bufstream.str(); } inline string amount_t::quantity_string() const { std::ostringstream bufstream; number().print(bufstream); return bufstream.str(); } inline std::ostream& operator<<(std::ostream& out, const amount_t& amt) { if (amount_t::stream_fullstrings) amt.unrounded().print(out); else amt.print(out); return out; } inline std::istream& operator>>(std::istream& in, amount_t& amt) { amt.parse(in); return in; } void put_amount(property_tree::ptree& pt, const amount_t& amt, bool commodity_details = false); } // namespace ledger #endif // INCLUDED_AMOUNT_H ��������������������������������������������������������ledger-3.3.2/src/annotate.cc������������������������������������������������������������������������0000664�0000000�0000000�00000026165�14411236400�0015675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "amount.h" #include "commodity.h" #include "expr.h" #include "annotate.h" #include "pool.h" namespace ledger { bool annotation_t::operator<(const annotation_t& rhs) const { if (! price && rhs.price) return true; if (price && ! rhs.price) return false; if (! date && rhs.date) return true; if (date && ! rhs.date) return false; if (! tag && rhs.tag) return true; if (tag && ! rhs.tag) return false; if (! value_expr && rhs.value_expr) return true; if (value_expr && ! rhs.value_expr) return false; if (price) { if (price->commodity().symbol() < rhs.price->commodity().symbol()) return true; if (price->commodity().symbol() > rhs.price->commodity().symbol()) return false; if (*price < *rhs.price) return true; if (*price > *rhs.price) return false; } if (date) { if (*date < *rhs.date) return true; if (*date > *rhs.date) return false; } if (tag) { if (*tag < *rhs.tag) return true; if (*tag > *rhs.tag) return false; } if (value_expr) { DEBUG("annotate.less", "Comparing (" << value_expr->text() << ") < (" << rhs.value_expr->text()); if (value_expr->text() < rhs.value_expr->text()) return true; //if (value_expr->text() > rhs.value_expr->text()) return false; } return false; } void annotation_t::parse(std::istream& in) { do { std::istream::pos_type pos = in.tellg(); if (static_cast<int>(pos) < 0) return; char buf[256]; int c = peek_next_nonws(in); if (c == '{') { if (price) throw_(amount_error, _("Commodity specifies more than one price")); in.get(); c = in.peek(); if (c == '{') { in.get(); add_flags(ANNOTATION_PRICE_NOT_PER_UNIT); } c = peek_next_nonws(in); if (c == '=') { in.get(); add_flags(ANNOTATION_PRICE_FIXATED); } READ_INTO(in, buf, 255, c, c != '}'); if (c == '}') { in.get(); if (has_flags(ANNOTATION_PRICE_NOT_PER_UNIT)) { c = in.peek(); if (c != '}') throw_(amount_error, _("Commodity lot price lacks double closing brace")); else in.get(); } } else { throw_(amount_error, _("Commodity lot price lacks closing brace")); } amount_t temp; temp.parse(buf, PARSE_NO_MIGRATE); DEBUG("commodity.annotations", "Parsed annotation price: " << temp); price = temp; } else if (c == '[') { if (date) throw_(amount_error, _("Commodity specifies more than one date")); in.get(); READ_INTO(in, buf, 255, c, c != ']'); if (c == ']') in.get(); else throw_(amount_error, _("Commodity date lacks closing bracket")); date = parse_date(buf); } else if (c == '(') { in.get(); c = in.peek(); if (c == '@') { in.clear(); in.seekg(pos, std::ios::beg); break; } else if (c == '(') { if (value_expr) throw_(amount_error, _("Commodity specifies more than one valuation expression")); in.get(); READ_INTO(in, buf, 255, c, c != ')'); if (c == ')') { in.get(); c = in.peek(); if (c == ')') in.get(); else throw_(amount_error, _("Commodity valuation expression lacks closing parentheses")); } else { throw_(amount_error, _("Commodity valuation expression lacks closing parentheses")); } value_expr = expr_t(buf); } else { if (tag) throw_(amount_error, _("Commodity specifies more than one tag")); READ_INTO(in, buf, 255, c, c != ')'); if (c == ')') in.get(); else throw_(amount_error, _("Commodity tag lacks closing parenthesis")); tag = buf; } } else { in.clear(); in.seekg(pos, std::ios::beg); break; } } while (true); #if DEBUG_ON if (SHOW_DEBUG("amount.commodities") && *this) { DEBUG("amount.commodities", "Parsed commodity annotations: " << std::endl << *this); } #endif } void annotation_t::print(std::ostream& out, bool keep_base, bool no_computed_annotations) const { if (price && (! no_computed_annotations || ! has_flags(ANNOTATION_PRICE_CALCULATED))) out << " {" << (has_flags(ANNOTATION_PRICE_FIXATED) ? "=" : "") << (keep_base ? *price : price->unreduced()) << '}'; if (date && (! no_computed_annotations || ! has_flags(ANNOTATION_DATE_CALCULATED))) out << " [" << format_date(*date, FMT_WRITTEN) << ']'; if (tag && (! no_computed_annotations || ! has_flags(ANNOTATION_TAG_CALCULATED))) out << " (" << *tag << ')'; if (value_expr && ! has_flags(ANNOTATION_VALUE_EXPR_CALCULATED)) out << " ((" << *value_expr << "))"; } void put_annotation(property_tree::ptree& st, const annotation_t& details) { if (details.price) put_amount(st.put("price", ""), *details.price); if (details.date) put_date(st.put("date", ""), *details.date); if (details.tag) st.put("tag", *details.tag); if (details.value_expr) st.put("value_expr", details.value_expr->text()); } bool keep_details_t::keep_all(const commodity_t& comm) const { return (! comm.has_annotation() || (keep_price && keep_date && keep_tag && ! only_actuals)); } bool keep_details_t::keep_any(const commodity_t& comm) const { return comm.has_annotation() && (keep_price || keep_date || keep_tag); } bool annotated_commodity_t::operator==(const commodity_t& comm) const { // If the base commodities don't match, the game's up. if (base != comm.base) return false; assert(annotated); if (! comm.annotated) return false; if (details != as_annotated_commodity(comm).details) return false; return true; } optional<price_point_t> annotated_commodity_t::find_price(const commodity_t * commodity, const datetime_t& moment, const datetime_t& oldest) const { DEBUG("commodity.price.find", "annotated_commodity_t::find_price(" << symbol() << ")"); datetime_t when; if (! moment.is_not_a_date_time()) when = moment; else if (epoch) when = *epoch; else when = CURRENT_TIME(); DEBUG("commodity.price.find", "reference time: " << when); const commodity_t * target = NULL; if (commodity) target = commodity; if (details.price) { DEBUG("commodity.price.find", "price annotation: " << *details.price); if (details.has_flags(ANNOTATION_PRICE_FIXATED)) { DEBUG("commodity.price.find", "amount_t::value: fixated price = " << *details.price); return price_point_t(when, *details.price); } else if (! target) { DEBUG("commodity.price.find", "setting target commodity from price"); target = details.price->commodity_ptr(); } } #if DEBUG_ON if (target) DEBUG("commodity.price.find", "target commodity: " << target->symbol()); #endif if (details.value_expr) return find_price_from_expr(const_cast<expr_t&>(*details.value_expr), commodity, when); return commodity_t::find_price(target, when, oldest); } commodity_t& annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) { DEBUG("commodity.annotated.strip", "Reducing commodity " << *this << std::endl << " keep price " << what_to_keep.keep_price << " " << " keep date " << what_to_keep.keep_date << " " << " keep tag " << what_to_keep.keep_tag); commodity_t * new_comm; bool keep_price = ((what_to_keep.keep_price || (details.has_flags(ANNOTATION_PRICE_FIXATED) && has_flags(COMMODITY_SAW_ANN_PRICE_FLOAT) && has_flags(COMMODITY_SAW_ANN_PRICE_FIXATED))) && (! what_to_keep.only_actuals || ! details.has_flags(ANNOTATION_PRICE_CALCULATED))); bool keep_date = (what_to_keep.keep_date && (! what_to_keep.only_actuals || ! details.has_flags(ANNOTATION_DATE_CALCULATED))); bool keep_tag = (what_to_keep.keep_tag && (! what_to_keep.only_actuals || ! details.has_flags(ANNOTATION_TAG_CALCULATED))); DEBUG("commodity.annotated.strip", "Reducing commodity " << *this << std::endl << " keep price " << keep_price << " " << " keep date " << keep_date << " " << " keep tag " << keep_tag); if ((keep_price && details.price) || (keep_date && details.date) || (keep_tag && details.tag)) { new_comm = pool().find_or_create (referent(), annotation_t(keep_price ? details.price : none, keep_date ? details.date : none, keep_tag ? details.tag : none)); // Transfer over any relevant annotation flags, as they still apply. if (new_comm->annotated) { annotation_t& new_details(as_annotated_commodity(*new_comm).details); if (keep_price) new_details.add_flags(details.flags() & (ANNOTATION_PRICE_CALCULATED | ANNOTATION_PRICE_FIXATED)); if (keep_date) new_details.add_flags(details.flags() & ANNOTATION_DATE_CALCULATED); if (keep_tag) new_details.add_flags(details.flags() & ANNOTATION_TAG_CALCULATED); } return *new_comm; } return referent(); } void annotated_commodity_t::write_annotations (std::ostream& out, bool no_computed_annotations) const { details.print(out, pool().keep_base, no_computed_annotations); } } // namespace ledger �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/annotate.h�������������������������������������������������������������������������0000664�0000000�0000000�00000016336�14411236400�0015536�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup math */ /** * @file annotate.h * @author John Wiegley * * @ingroup math * * @brief Types for annotating commodities * * Long. */ #ifndef INCLUDED_ANNOTATE_H #define INCLUDED_ANNOTATE_H #include "expr.h" namespace ledger { struct annotation_t : public supports_flags<>, public equality_comparable<annotation_t> { #define ANNOTATION_PRICE_CALCULATED 0x01 #define ANNOTATION_PRICE_FIXATED 0x02 #define ANNOTATION_PRICE_NOT_PER_UNIT 0x04 #define ANNOTATION_DATE_CALCULATED 0x08 #define ANNOTATION_TAG_CALCULATED 0x10 #define ANNOTATION_VALUE_EXPR_CALCULATED 0x20 optional<amount_t> price; optional<date_t> date; optional<string> tag; optional<expr_t> value_expr; explicit annotation_t(const optional<amount_t>& _price = none, const optional<date_t>& _date = none, const optional<string>& _tag = none, const optional<expr_t>& _value_expr = none) : supports_flags<>(), price(_price), date(_date), tag(_tag), value_expr(_value_expr) { TRACE_CTOR(annotation_t, "optional<amount_t> + date_t + string + expr_t"); } annotation_t(const annotation_t& other) : supports_flags<>(other.flags()), price(other.price), date(other.date), tag(other.tag), value_expr(other.value_expr) { TRACE_CTOR(annotation_t, "copy"); } ~annotation_t() { TRACE_DTOR(annotation_t); } operator bool() const { return price || date || tag || value_expr; } bool operator<(const annotation_t& rhs) const; bool operator==(const annotation_t& rhs) const { return (price == rhs.price && date == rhs.date && tag == rhs.tag && (value_expr && rhs.value_expr ? value_expr->text() == rhs.value_expr->text() : value_expr == rhs.value_expr)); } void parse(std::istream& in); void print(std::ostream& out, bool keep_base = false, bool no_computed_annotations = false) const; bool valid() const { assert(*this); return true; } }; void put_annotation(property_tree::ptree& pt, const annotation_t& details); struct keep_details_t { bool keep_price; bool keep_date; bool keep_tag; bool only_actuals; explicit keep_details_t(bool _keep_price = false, bool _keep_date = false, bool _keep_tag = false, bool _only_actuals = false) : keep_price(_keep_price), keep_date(_keep_date), keep_tag(_keep_tag), only_actuals(_only_actuals) { TRACE_CTOR(keep_details_t, "bool, bool, bool, bool"); } keep_details_t(const keep_details_t& other) : keep_price(other.keep_price), keep_date(other.keep_date), keep_tag(other.keep_tag), only_actuals(other.only_actuals) { TRACE_CTOR(keep_details_t, "copy"); } ~keep_details_t() throw() { TRACE_DTOR(keep_details_t); } bool keep_all() const { return keep_price && keep_date && keep_tag && ! only_actuals; } bool keep_all(const commodity_t& comm) const; bool keep_any() const { return keep_price || keep_date || keep_tag; } bool keep_any(const commodity_t& comm) const; }; inline std::ostream& operator<<(std::ostream& out, const annotation_t& details) { details.print(out); return out; } class annotated_commodity_t : public commodity_t, public equality_comparable<annotated_commodity_t, equality_comparable2<annotated_commodity_t, commodity_t, noncopyable> > { protected: friend class commodity_pool_t; commodity_t * ptr; explicit annotated_commodity_t(commodity_t * _ptr, const annotation_t& _details) : commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) { annotated = true; qualified_symbol = _ptr->qualified_symbol; TRACE_CTOR(annotated_commodity_t, "commodity_t *, annotation_t"); } public: annotation_t details; virtual ~annotated_commodity_t() { TRACE_DTOR(annotated_commodity_t); } virtual bool operator==(const commodity_t& comm) const; virtual bool operator==(const annotated_commodity_t& comm) const { return *this == static_cast<const commodity_t&>(comm); } virtual commodity_t& referent() { return *ptr; } virtual const commodity_t& referent() const { return *ptr; } virtual optional<expr_t> value_expr() const { if (details.value_expr) return details.value_expr; return commodity_t::value_expr(); } optional<price_point_t> virtual find_price(const commodity_t * commodity = NULL, const datetime_t& moment = datetime_t(), const datetime_t& oldest = datetime_t()) const; virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep); virtual void print(std::ostream& out, bool elide_quotes = false, bool print_annotations = false) const { if (print_annotations) { std::ostringstream buf; commodity_t::print(buf, elide_quotes); write_annotations(buf); out << buf.str(); } else { commodity_t::print(out, elide_quotes); } } virtual void write_annotations(std::ostream& out, bool no_computed_annotations = false) const; }; inline annotated_commodity_t& as_annotated_commodity(commodity_t& commodity) { return downcast<annotated_commodity_t>(commodity); } inline const annotated_commodity_t& as_annotated_commodity(const commodity_t& commodity) { return downcast<const annotated_commodity_t>(commodity); } } // namespace ledger #endif // INCLUDED_ANNOTATE_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/balance.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000030655�14411236400�0015450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "balance.h" #include "commodity.h" #include "annotate.h" #include "pool.h" #include "unistring.h" // for justify() namespace ledger { balance_t::balance_t(const double val) { amounts.insert (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val)); TRACE_CTOR(balance_t, "const double"); } balance_t::balance_t(const unsigned long val) { amounts.insert (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val)); TRACE_CTOR(balance_t, "const unsigned long"); } balance_t::balance_t(const long val) { amounts.insert (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val)); TRACE_CTOR(balance_t, "const long"); } balance_t& balance_t::operator+=(const balance_t& bal) { foreach (const amounts_map::value_type& pair, bal.amounts) *this += pair.second; return *this; } balance_t& balance_t::operator+=(const amount_t& amt) { if (amt.is_null()) throw_(balance_error, _("Cannot add an uninitialized amount to a balance")); if (amt.is_realzero()) return *this; amounts_map::iterator i = amt.commodity().has_annotation() ? find_by_name(amt.commodity()) : amounts.find(&amt.commodity()); if (i != amounts.end()) i->second += amt; else amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); return *this; } balance_t& balance_t::operator-=(const balance_t& bal) { foreach (const amounts_map::value_type& pair, bal.amounts) *this -= pair.second; return *this; } balance_t& balance_t::operator-=(const amount_t& amt) { if (amt.is_null()) throw_(balance_error, _("Cannot subtract an uninitialized amount from a balance")); if (amt.is_realzero()) return *this; amounts_map::iterator i = amt.commodity().has_annotation() ? find_by_name(amt.commodity()) : amounts.find(&amt.commodity()); if (i != amounts.end()) { i->second -= amt; if (i->second.is_realzero()) amounts.erase(i); } else { amounts.insert(amounts_map::value_type(&amt.commodity(), amt.negated())); } return *this; } balance_t& balance_t::operator*=(const amount_t& amt) { if (amt.is_null()) throw_(balance_error, _("Cannot multiply a balance by an uninitialized amount")); if (is_realzero()) { ; } else if (amt.is_realzero()) { *this = amt; } else if (! amt.commodity()) { // Multiplying by an amount with no commodity causes all the // component amounts to be increased by the same factor. foreach (amounts_map::value_type& pair, amounts) pair.second *= amt; } else if (amounts.size() == 1) { // Multiplying by a commoditized amount is only valid if the sole // commodity in the balance is of the same kind as the amount's // commodity. if (*amounts.begin()->first == amt.commodity()) amounts.begin()->second *= amt; else throw_(balance_error, _("Cannot multiply a balance with annotated commodities by a commoditized amount")); } else { assert(amounts.size() > 1); throw_(balance_error, _("Cannot multiply a multi-commodity balance by a commoditized amount")); } return *this; } balance_t& balance_t::operator/=(const amount_t& amt) { if (amt.is_null()) throw_(balance_error, _("Cannot divide a balance by an uninitialized amount")); if (is_realzero()) { ; } else if (amt.is_realzero()) { throw_(balance_error, _("Divide by zero")); } else if (! amt.commodity()) { // Dividing by an amount with no commodity causes all the // component amounts to be divided by the same factor. foreach (amounts_map::value_type& pair, amounts) pair.second /= amt; } else if (amounts.size() == 1) { // Dividing by a commoditized amount is only valid if the sole // commodity in the balance is of the same kind as the amount's // commodity. if (*amounts.begin()->first == amt.commodity()) amounts.begin()->second /= amt; else throw_(balance_error, _("Cannot divide a balance with annotated commodities by a commoditized amount")); } else { assert(amounts.size() > 1); throw_(balance_error, _("Cannot divide a multi-commodity balance by a commoditized amount")); } return *this; } optional<balance_t> balance_t::value(const datetime_t& moment, const commodity_t * in_terms_of) const { balance_t temp; bool resolved = false; foreach (const amounts_map::value_type& pair, amounts) { if (optional<amount_t> val = pair.second.value(moment, in_terms_of)) { temp += *val; resolved = true; } else { temp += pair.second; } } return resolved ? temp : optional<balance_t>(); } balance_t::amounts_map::iterator balance_t::find_by_name(const commodity_t& comm) { for (amounts_map::iterator i = amounts.begin(); i != amounts.end(); i++) { if (*(*i).first == comm) return i; } return amounts.end(); } balance_t::amounts_map::const_iterator balance_t::find_by_name(const commodity_t& comm) const { for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); i++) { if (*(*i).first == comm) return i; } return amounts.end(); } optional<amount_t> balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const { if (! commodity) { if (amounts.size() == 1) { return amounts.begin()->second; } else if (amounts.size() > 1) { // Try stripping annotations before giving an error. balance_t temp(strip_annotations(keep_details_t())); if (temp.amounts.size() == 1) return temp.commodity_amount(commodity); throw_(amount_error, _f("Requested amount of a balance with multiple commodities: %1%") % temp); } } else if (amounts.size() > 0) { amounts_map::const_iterator i = commodity->has_annotation() ? find_by_name(*commodity) : amounts.find(const_cast<commodity_t *>(&*commodity)); if (i != amounts.end()) return i->second; } return none; } balance_t balance_t::strip_annotations(const keep_details_t& what_to_keep) const { balance_t temp; foreach (const amounts_map::value_type& pair, amounts) temp += pair.second.strip_annotations(what_to_keep); return temp; } void balance_t::sorted_amounts(amounts_array& sorted) const { foreach (const amounts_map::value_type& pair, amounts) if (pair.second) sorted.push_back(&pair.second); std::stable_sort( sorted.begin(), sorted.end(), [](const amount_t * left, const amount_t * right) { return commodity_t::compare_by_commodity()(left, right) < 0; }); } void balance_t::map_sorted_amounts(function<void(const amount_t&)> fn) const { if (! amounts.empty()) { if (amounts.size() == 1) { const amount_t& amount((*amounts.begin()).second); if (amount) fn(amount); } else { amounts_array sorted; sorted_amounts(sorted); foreach (const amount_t * amount, sorted) fn(*amount); } } } namespace { struct print_amount_from_balance { std::ostream& out; bool& first; int fwidth; int lwidth; uint_least8_t flags; explicit print_amount_from_balance(std::ostream& _out, bool& _first, int _fwidth, int _lwidth, uint_least8_t _flags) : out(_out), first(_first), fwidth(_fwidth), lwidth(_lwidth), flags(_flags) { TRACE_CTOR(print_amount_from_balance, "ostream&, int, int, uint_least8_t"); } print_amount_from_balance(const print_amount_from_balance& other) : out(other.out), first(other.first), fwidth(other.fwidth), lwidth(other.lwidth), flags(other.flags) { TRACE_CTOR(print_amount_from_balance, "copy"); } ~print_amount_from_balance() throw() { TRACE_DTOR(print_amount_from_balance); } void operator()(const amount_t& amount) { int width; if (! first) { out << std::endl; width = lwidth; } else { first = false; width = fwidth; } std::ostringstream buf; amount.print(buf, flags); justify(out, buf.str(), width, flags & AMOUNT_PRINT_RIGHT_JUSTIFY, flags & AMOUNT_PRINT_COLORIZE && amount.sign() < 0); } void close() { out.width(fwidth); if (flags & AMOUNT_PRINT_RIGHT_JUSTIFY) out << std::right; else out << std::left; out << 0; } }; } void balance_t::print(std::ostream& out, const int first_width, const int latter_width, const uint_least8_t flags) const { bool first = true; print_amount_from_balance amount_printer(out, first, first_width, latter_width == 1 ? first_width : latter_width, flags); map_sorted_amounts(amount_printer); if (first) amount_printer.close(); } void put_balance(property_tree::ptree& st, const balance_t& bal) { foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) put_amount(st.add("amount", ""), pair.second); } balance_t average_lot_prices(const balance_t& bal) { // First, we split the balance into multiple balances by underlying // commodity. typedef std::map<optional<std::string>, std::pair<amount_t, annotation_t> > balance_map; balance_map bycomm; foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { optional<std::string> sym(pair.first->symbol()); amount_t quant(pair.second.strip_annotations(keep_details_t())); balance_map::iterator i = bycomm.find(sym); if (i == bycomm.end()) { bycomm.insert( balance_map::value_type(sym, std::make_pair(quant, annotation_t()))); i = bycomm.find(sym); // must succeed now } else { (*i).second.first += quant; } if (pair.first->has_annotation()) { annotated_commodity_t& acomm(static_cast<annotated_commodity_t&>(*pair.first)); annotation_t& ann((*i).second.second); if (acomm.details.price) { if (ann.price) ann.price = *ann.price + (*acomm.details.price * quant); else ann.price = *acomm.details.price * quant; } if (acomm.details.date) { if (! ann.date || *acomm.details.date < *ann.date) ann.date = *acomm.details.date; } } } balance_t result; foreach (balance_map::value_type& pair, bycomm) { amount_t amt(pair.second.first); if (! amt.is_realzero()) { if (pair.second.second.price) pair.second.second.price = *pair.second.second.price / amt; commodity_t * acomm = commodity_pool_t::current_pool->find_or_create( amt.commodity(), pair.second.second); amt.set_commodity(*acomm); result += amt; } } return result; } } // namespace ledger �����������������������������������������������������������������������������������ledger-3.3.2/src/balance.h��������������������������������������������������������������������������0000664�0000000�0000000�00000045473�14411236400�0015316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup math */ /** * @file balance.h * @author John Wiegley * * @ingroup math * * @brief Basic type for adding multiple commodities together * * Unlike the amount_t class, which throws an exception if amounts of * differing commodities are added or subtracted, the balance_t class * is designed to allow this, tracking the amounts of each component * commodity separately. */ #ifndef INCLUDED_BALANCE_H #define INCLUDED_BALANCE_H #include "amount.h" namespace ledger { DECLARE_EXCEPTION(balance_error, std::runtime_error); /** * @class balance_t * * @brief A wrapper around amount_t allowing addition of multiple commodities. * * The balance_t class is appropriate for keeping a running balance * where amounts of multiple commodities may be involved. */ class balance_t : public equality_comparable<balance_t, equality_comparable<balance_t, amount_t, equality_comparable<balance_t, double, equality_comparable<balance_t, unsigned long, equality_comparable<balance_t, long, additive<balance_t, additive<balance_t, amount_t, additive<balance_t, double, additive<balance_t, unsigned long, additive<balance_t, long, multiplicative<balance_t, amount_t, multiplicative<balance_t, double, multiplicative<balance_t, unsigned long, multiplicative<balance_t, long> > > > > > > > > > > > > > { public: typedef std::unordered_map<commodity_t *, amount_t> amounts_map; typedef std::vector<const amount_t *> amounts_array; amounts_map amounts; /** * Constructors. balance_t supports similar forms of construction * to amount_t. * * balance_t() creates an empty balance to which amounts or other * balances may be added or subtracted. * * balance_t(amount_t) constructs a balance whose starting value is * equal to the given amount. * * balance_t(double), balance_t(unsigned long) and balance_t(long) * will construct an amount from their arguments and then construct * a balance whose starting value is equal to that amount. This * initial balance will have no commodity. * * balance_t(string) and balance_t(const char *) both convert from a * string representation of an amount to a balance whose initial * value is that amount. This is the proper way to initialize a * balance like '$100.00'. */ balance_t() { TRACE_CTOR(balance_t, ""); } balance_t(const amount_t& amt) { if (amt.is_null()) throw_(balance_error, _("Cannot initialize a balance from an uninitialized amount")); if (! amt.is_realzero()) amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); TRACE_CTOR(balance_t, "const amount_t&"); } balance_t(const double val); balance_t(const unsigned long val); balance_t(const long val); explicit balance_t(const string& val) { amount_t temp(val); amounts.insert(amounts_map::value_type(&temp.commodity(), temp)); TRACE_CTOR(balance_t, "const string&"); } explicit balance_t(const char * val) { amount_t temp(val); amounts.insert(amounts_map::value_type(&temp.commodity(), temp)); TRACE_CTOR(balance_t, "const char *"); } /** * Destructor. Destroys all of the accumulated amounts in the * balance. */ ~balance_t() { TRACE_DTOR(balance_t); } /** * Assignment and copy operators. An balance may be assigned or copied. */ balance_t(const balance_t& bal) : amounts(bal.amounts) { TRACE_CTOR(balance_t, "copy"); } balance_t& operator=(const balance_t& bal) { if (this != &bal) amounts = bal.amounts; return *this; } balance_t& operator=(const amount_t& amt) { if (amt.is_null()) throw_(balance_error, _("Cannot assign an uninitialized amount to a balance")); amounts.clear(); if (! amt.is_realzero()) amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); return *this; } balance_t& operator=(const string& str) { return *this = balance_t(str); } balance_t& operator=(const char * str) { return *this = balance_t(str); } /** * Comparison operators. Balances are fairly restrictive in terms * of how they may be compared. They may be compared for equality * or inequality, but this is all, since the concept of "less than" * or "greater than" makes no sense when amounts of multiple * commodities are involved. * * Balances may also be compared to amounts, in which case the sum * of the balance must equal the amount exactly. * * If a comparison between balances is desired, the balances must * first be rendered to value equivalent amounts using the `value' * method, to determine a market valuation at some specific moment * in time. */ bool operator==(const balance_t& bal) const { return amounts == bal.amounts; } bool operator==(const amount_t& amt) const { if (amt.is_null()) throw_(balance_error, _("Cannot compare a balance to an uninitialized amount")); if (amt.is_realzero()) return amounts.empty(); else return amounts.size() == 1 && amounts.begin()->second == amt; } template <typename T> bool operator==(const T& val) const { return *this == amount_t(val); } /** * Binary arithmetic operators. Balances support addition and * subtraction of other balances or amounts, but multiplication and * division are restricted to uncommoditized amounts only. */ balance_t& operator+=(const balance_t& bal); balance_t& operator+=(const amount_t& amt); balance_t& operator+=(const double val) { return *this += amount_t(val); } balance_t& operator+=(const unsigned long val) { return *this += amount_t(val); } balance_t& operator+=(const long val) { return *this += amount_t(val); } balance_t& operator-=(const balance_t& bal); balance_t& operator-=(const amount_t& amt); balance_t& operator-=(const double val) { return *this -= amount_t(val); } balance_t& operator-=(const unsigned long val) { return *this -= amount_t(val); } balance_t& operator-=(const long val) { return *this -= amount_t(val); } balance_t& operator*=(const amount_t& amt); balance_t& operator*=(const double val) { return *this *= amount_t(val); } balance_t& operator*=(const unsigned long val) { return *this *= amount_t(val); } balance_t& operator*=(const long val) { return *this *= amount_t(val); } balance_t& operator/=(const amount_t& amt); balance_t& operator/=(const double val) { return *this /= amount_t(val); } balance_t& operator/=(const unsigned long val) { return *this /= amount_t(val); } balance_t& operator/=(const long val) { return *this /= amount_t(val); } /** * Unary arithmetic operators. There are only a few unary methods * support on balance: * * negate(), also unary minus (- x), returns a balance all of whose * component amounts have been negated. In order words, it inverts * the sign of all member amounts. * * abs() returns a balance where no component amount is negative. * * reduce() reduces the values in a balance to their most basic * commodity forms, for amounts that utilize "scaling commodities". * For example, a balance of 1h and 1m after reduction will be * 3660s. * * unreduce(), if used with amounts that use "scaling commodities", * yields the most compact form greater than 1.0 for each component * amount. That is, a balance of 10m and 1799s will unreduce to * 39.98m. * * value(optional<datetime_t>) returns the total historical value for * a balance -- the default moment returns a value based on the most * recently known price -- based on the price history of its * component commodities. See amount_t::value for an example. * * Further, for the sake of efficiency and avoiding temporary * objects, the following methods support "in-place" variants act on * the balance itself and return a reference to the result * (`*this'): * * in_place_negate() * in_place_reduce() * in_place_unreduce() */ balance_t negated() const { balance_t temp(*this); temp.in_place_negate(); return temp; } void in_place_negate() { foreach (amounts_map::value_type& pair, amounts) pair.second.in_place_negate(); } balance_t operator-() const { return negated(); } balance_t abs() const { balance_t temp; foreach (const amounts_map::value_type& pair, amounts) temp += pair.second.abs(); return temp; } balance_t rounded() const { balance_t temp(*this); temp.in_place_round(); return temp; } void in_place_round() { foreach (amounts_map::value_type& pair, amounts) pair.second.in_place_round(); } balance_t roundto(int places) const { balance_t temp(*this); temp.in_place_roundto(places); return temp; } void in_place_roundto(int places) { foreach (amounts_map::value_type& pair, amounts) pair.second.in_place_roundto(places); } balance_t truncated() const { balance_t temp(*this); temp.in_place_truncate(); return temp; } void in_place_truncate() { foreach (amounts_map::value_type& pair, amounts) pair.second.in_place_truncate(); } balance_t floored() const { balance_t temp(*this); temp.in_place_floor(); return temp; } void in_place_floor() { foreach (amounts_map::value_type& pair, amounts) pair.second.in_place_floor(); } balance_t ceilinged() const { balance_t temp(*this); temp.in_place_ceiling(); return temp; } void in_place_ceiling() { foreach (amounts_map::value_type& pair, amounts) pair.second.in_place_ceiling(); } balance_t unrounded() const { balance_t temp(*this); temp.in_place_unround(); return temp; } void in_place_unround() { foreach (amounts_map::value_type& pair, amounts) pair.second.in_place_unround(); } balance_t reduced() const { balance_t temp(*this); temp.in_place_reduce(); return temp; } void in_place_reduce() { // A temporary must be used here because reduction may cause // multiple component amounts to collapse to the same commodity. balance_t temp; foreach (const amounts_map::value_type& pair, amounts) temp += pair.second.reduced(); *this = temp; } balance_t unreduced() const { balance_t temp(*this); temp.in_place_unreduce(); return temp; } void in_place_unreduce() { // A temporary must be used here because unreduction may cause // multiple component amounts to collapse to the same commodity. balance_t temp; foreach (const amounts_map::value_type& pair, amounts) temp += pair.second.unreduced(); *this = temp; } optional<balance_t> value(const datetime_t& moment = datetime_t(), const commodity_t * in_terms_of = NULL) const; /** * Truth tests. An balance may be truth test in two ways: * * is_nonzero(), or operator bool, returns true if a balance's * display value is not zero. * * is_zero() returns true if an balance's display value is zero. * Thus, a balance containing $0.0001 is considered zero if the * current display precision for dollars is two decimal places. * * is_realzero() returns true if an balance's actual value is zero. * Thus, a balance containing $0.0001 is never considered realzero. * * is_empty() returns true if a balance has no amounts within it. * This can occur after a balance has been default initialized, or * if the exact amount it contains is subsequently subtracted from * it. */ operator bool() const { return is_nonzero(); } bool is_nonzero() const { if (is_empty()) return false; foreach (const amounts_map::value_type& pair, amounts) if (pair.second.is_nonzero()) return true; return false; } bool is_zero() const { if (is_empty()) return true; foreach (const amounts_map::value_type& pair, amounts) if (! pair.second.is_zero()) return false; return true; } bool is_realzero() const { if (is_empty()) return true; foreach (const amounts_map::value_type& pair, amounts) if (! pair.second.is_realzero()) return false; return true; } bool is_empty() const { return amounts.size() == 0; } bool single_amount() const { return amounts.size() == 1; } /** * Conversion methods. A balance can be converted to an amount, but * only if contains a single component amount. */ operator string() const { return to_string(); } string to_string() const { std::ostringstream buf; print(buf); return buf.str(); } amount_t to_amount() const { if (is_empty()) throw_(balance_error, _("Cannot convert an empty balance to an amount")); else if (amounts.size() == 1) return amounts.begin()->second; else throw_(balance_error, _("Cannot convert a balance with multiple commodities to an amount")); return amount_t(); } /** * Commodity-related methods. Balances support two * commodity-related methods: * * commodity_count() returns the number of different commodities * stored in the balance. * * commodity_amount(optional<commodity_t>) returns an (optional) * amount for the given commodity within the balance; if no * commodity is specified, it returns the (optional) uncommoditized * component of the balance. If no matching element can be found, * boost::none is returned. */ std::size_t commodity_count() const { return amounts.size(); } optional<amount_t> commodity_amount(const optional<const commodity_t&>& commodity = none) const; amounts_map::iterator find_by_name(const commodity_t& comm); amounts_map::const_iterator find_by_name(const commodity_t& comm) const; balance_t number() const { balance_t temp; foreach (const amounts_map::value_type& pair, amounts) temp += pair.second.number(); return temp; } /** * Annotated commodity methods. The amounts contained by a balance * may use annotated commodities. The `strip_annotations' method * will return a balance all of whose component amount have had * their commodity annotations likewise stripped. See * amount_t::strip_annotations for more details. */ balance_t strip_annotations(const keep_details_t& what_to_keep) const; /** * Given a balance, insert a commodity-wise sort of the amounts into the * given amounts_array. */ void sorted_amounts(amounts_array& sorted) const; /** * Iteration primitives. `map_sorted_amounts' allows one to visit * each amount in balance in the proper order for displaying to the * user. Mostly used by `print' and other routinse where the sort * order of the amounts' commodities is significant. */ void map_sorted_amounts(function<void(const amount_t&)> fn) const; /** * Printing methods. A balance may be output to a stream using the * `print' method. There is also a global operator<< defined which * simply calls print for a balance on the given stream. There is * one form of the print method, which takes two required arguments * and one arguments with a default value: * * print(ostream, int first_width, int latter_width) prints a * balance to the given output stream, using each commodity's * default display characteristics. The first_width parameter * specifies the width that should be used for printing amounts * (since they are likely to vary in width). The latter_width, if * specified, gives the width to be used for each line after the * first. This is useful when printing in a column which falls at * the right-hand side of the screen. * * In addition to the width constraints, balances will also print * with commodities in alphabetized order, regardless of the * relative amounts of those commodities. There is no option to * change this behavior. */ void print(std::ostream& out, const int first_width = -1, const int latter_width = -1, const uint_least8_t flags = AMOUNT_PRINT_NO_FLAGS) const; /** * Debugging methods. There are two methods defined to help with * debugging: * * dump(ostream) dumps a balance to an output stream. There is * little different from print(), it simply surrounds the display * value with a marker, for example "BALANCE($1.00, DM 12.00)". * This code is used by other dumping code elsewhere in Ledger. * * valid() returns true if the amounts within the balance are valid. */ void dump(std::ostream& out) const { out << "BALANCE("; bool first = true; foreach (const amounts_map::value_type& pair, amounts) { if (first) first = false; else out << ", "; pair.second.print(out); } out << ")"; } bool valid() const { foreach (const amounts_map::value_type& pair, amounts) if (! pair.second.valid()) { DEBUG("ledger.validate", "balance_t: ! pair.second.valid()"); return false; } return true; } }; inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) { bal.print(out, 12); return out; } void put_balance(property_tree::ptree& pt, const balance_t& bal); balance_t average_lot_prices(const balance_t& bal); } // namespace ledger #endif // INCLUDED_BALANCE_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/chain.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000027511�14411236400�0015142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "chain.h" #include "predicate.h" #include "filters.h" #include "report.h" #include "session.h" namespace ledger { post_handler_ptr chain_pre_post_handlers(post_handler_ptr base_handler, report_t& report) { post_handler_ptr handler(base_handler); // anonymize_posts removes all meaningful information from xact payee's and // account names, for the sake of creating useful bug reports. if (report.HANDLED(anon)) handler.reset(new anonymize_posts(handler)); // This filter_posts will only pass through posts matching the `predicate'. if (report.HANDLED(limit_)) { DEBUG("report.predicate", "Report predicate expression = " << report.HANDLER(limit_).str()); handler.reset(new filter_posts (handler, predicate_t(report.HANDLER(limit_).str(), report.what_to_keep()), report)); } // budget_posts takes a set of posts from a data file and uses them to // generate "budget posts" which balance against the reported posts. // // forecast_posts is a lot like budget_posts, except that it adds xacts // only for the future, and does not balance them against anything but the // future balance. if (report.budget_flags != BUDGET_NO_BUDGET) { budget_posts * budget_handler = new budget_posts(handler, report.terminus.date(), report.budget_flags); budget_handler->add_period_xacts(report.session.journal->period_xacts); handler.reset(budget_handler); // Apply this before the budget handler, so that only matching posts are // calculated toward the budget. The use of filter_posts above will // further clean the results so that no automated posts that don't match // the filter get reported. if (report.HANDLED(limit_)) handler.reset(new filter_posts (handler, predicate_t(report.HANDLER(limit_).str(), report.what_to_keep()), report)); } else if (report.HANDLED(forecast_while_)) { forecast_posts * forecast_handler = new forecast_posts(handler, predicate_t(report.HANDLER(forecast_while_).str(), report.what_to_keep()), report, (report.HANDLED(forecast_years_) ? lexical_cast<std::size_t> (report.HANDLER(forecast_years_).value) : 5UL)); forecast_handler->add_period_xacts(report.session.journal->period_xacts); handler.reset(forecast_handler); // See above, under budget_posts. if (report.HANDLED(limit_)) handler.reset(new filter_posts (handler, predicate_t(report.HANDLER(limit_).str(), report.what_to_keep()), report)); } return handler; } post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, report_t& report, bool for_accounts_report) { post_handler_ptr handler(base_handler); predicate_t display_predicate; predicate_t only_predicate; display_filter_posts * display_filter = NULL; expr_t& expr(report.HANDLER(amount_).expr); expr.set_context(&report); report.HANDLER(total_).expr.set_context(&report); report.HANDLER(display_amount_).expr.set_context(&report); report.HANDLER(display_total_).expr.set_context(&report); if (! for_accounts_report) { // Make sure only forecast postings which match are allowed through if (report.HANDLED(forecast_while_)) { handler.reset(new filter_posts (handler, predicate_t(report.HANDLER(forecast_while_).str(), report.what_to_keep()), report)); } // truncate_xacts cuts off a certain number of _xacts_ from being // displayed. It does not affect calculation. if (report.HANDLED(head_) || report.HANDLED(tail_)) handler.reset (new truncate_xacts(handler, report.HANDLED(head_) ? lexical_cast<int>(report.HANDLER(head_).value) : 0, report.HANDLED(tail_) ? lexical_cast<int>(report.HANDLER(tail_).value) : 0)); // display_filter_posts adds virtual posts to the list to account // for changes in value of commodities, which otherwise would affect // the running total unpredictably. display_filter = new display_filter_posts(handler, report, report.HANDLED(revalued) && ! report.HANDLED(no_rounding)); handler.reset(display_filter); // filter_posts will only pass through posts matching the // `display_predicate'. if (report.HANDLED(display_)) { display_predicate = predicate_t(report.HANDLER(display_).str(), report.what_to_keep()); handler.reset(new filter_posts(handler, display_predicate, report)); } } // changed_value_posts adds virtual posts to the list to account for changes // in market value of commodities, which otherwise would affect the running // total unpredictably. if (report.HANDLED(revalued) && (! for_accounts_report || report.HANDLED(unrealized))) handler.reset(new changed_value_posts(handler, report, for_accounts_report, report.HANDLED(unrealized), display_filter)); // calc_posts computes the running total. When this appears will determine, // for example, whether filtered posts are included or excluded from the // running total. handler.reset(new calc_posts(handler, expr, (! for_accounts_report || (report.HANDLED(revalued) && report.HANDLED(unrealized))))); // filter_posts will only pass through posts matching the // `secondary_predicate'. if (report.HANDLED(only_)) { only_predicate = predicate_t(report.HANDLER(only_).str(), report.what_to_keep()); handler.reset(new filter_posts(handler, only_predicate, report)); } if (! for_accounts_report) { // sort_posts will sort all the posts it sees, based on the `sort_order' // value expression. if (report.HANDLED(sort_)) { if (report.HANDLED(sort_xacts_)) handler.reset(new sort_xacts(handler, expr_t(report.HANDLER(sort_).str()), report)); else handler.reset(new sort_posts(handler, report.HANDLER(sort_).str(), report)); } // collapse_posts causes xacts with multiple posts to appear as xacts // with a subtotaled post for each commodity used. if (report.HANDLED(collapse) || report.HANDLED(depth_)) { unsigned short collapse_depth = 0; if (report.HANDLED(depth_)) collapse_depth = lexical_cast<int>(report.HANDLER(depth_).str()); handler.reset(new collapse_posts(handler, report, expr, display_predicate, only_predicate, report.HANDLED(collapse_if_zero), collapse_depth)); } // subtotal_posts combines all the posts it receives into one subtotal // xact, which has one post for each commodity in each account. // // period_posts is like subtotal_posts, but it subtotals according to time // periods rather than totaling everything. // // day_of_week_posts is like period_posts, except that it reports // all the posts that fall on each subsequent day of the week. if (report.HANDLED(equity)) handler.reset(new posts_as_equity(handler, report, expr, report.HANDLED(unround))); else if (report.HANDLED(subtotal)) handler.reset(new subtotal_posts(handler, expr)); } if (report.HANDLED(dow)) handler.reset(new day_of_week_posts(handler, expr)); else if (report.HANDLED(by_payee)) handler.reset(new by_payee_posts(handler, expr)); // interval_posts groups posts together based on a time period, such as // weekly or monthly. if (report.HANDLED(period_)) handler.reset(new interval_posts(handler, expr, report.HANDLER(period_).str(), report.HANDLED(exact), report.HANDLED(empty))); if (report.HANDLED(date_)) handler.reset(new transfer_details(handler, transfer_details::SET_DATE, report.session.journal->master, report.HANDLER(date_).str(), report)); if (report.HANDLED(account_)) { handler.reset(new transfer_details(handler, transfer_details::SET_ACCOUNT, report.session.journal->master, report.HANDLER(account_).str(), report)); } else if (report.HANDLED(pivot_)) { string pivot = report.HANDLER(pivot_).str(); pivot = string("\"") + pivot + ":\" + tag(\"" + pivot + "\")"; handler.reset(new transfer_details(handler, transfer_details::SET_ACCOUNT, report.session.journal->master, pivot, report)); } if (report.HANDLED(payee_)) handler.reset(new transfer_details(handler, transfer_details::SET_PAYEE, report.session.journal->master, report.HANDLER(payee_).str(), report)); // related_posts will pass along all posts related to the post received. If // the `related_all' handler is on, then all the xact's posts are passed; // meaning that if one post of an xact is to be printed, all the post for // that xact will be printed. if (report.HANDLED(related)) handler.reset(new related_posts(handler, report.HANDLED(related_all))); if (report.HANDLED(inject_)) handler.reset(new inject_posts(handler, report.HANDLED(inject_).str(), report.session.journal->master)); return handler; } } // namespace ledger ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/chain.h����������������������������������������������������������������������������0000664�0000000�0000000�00000006415�14411236400�0015004�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup report */ /** * @file chain.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_CHAIN_H #define INCLUDED_CHAIN_H #include "utils.h" namespace ledger { class post_t; class account_t; template <typename T> class item_handler : public noncopyable { protected: shared_ptr<item_handler> handler; public: item_handler() { TRACE_CTOR(item_handler, ""); } item_handler(shared_ptr<item_handler> _handler) : handler(_handler) { TRACE_CTOR(item_handler, "shared_ptr<item_handler>"); } virtual ~item_handler() { TRACE_DTOR(item_handler); } virtual void title(const string& str) { if (handler) handler->title(str); } virtual void flush() { if (handler) handler->flush(); } virtual void operator()(T& item) { if (handler) { check_for_signal(); (*handler)(item); } } virtual void clear() { if (handler) handler->clear(); } }; typedef shared_ptr<item_handler<post_t> > post_handler_ptr; typedef shared_ptr<item_handler<account_t> > acct_handler_ptr; class report_t; post_handler_ptr chain_pre_post_handlers(post_handler_ptr base_handler, report_t& report); post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, report_t& report, bool for_accounts_report = false); inline post_handler_ptr chain_handlers(post_handler_ptr handler, report_t& report, bool for_accounts_report = false) { handler = chain_post_handlers(handler, report, for_accounts_report); handler = chain_pre_post_handlers(handler, report); return handler; } } // namespace ledger #endif // INCLUDED_CHAIN_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/commodity.cc�����������������������������������������������������������������������0000664�0000000�0000000�00000042645�14411236400�0016071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "amount.h" #include "commodity.h" #include "annotate.h" #include "pool.h" #include "scope.h" namespace ledger { bool commodity_t::decimal_comma_by_default = false; bool commodity_t::time_colon_by_default = false; void commodity_t::add_price(const datetime_t& date, const amount_t& price, const bool reflexive) { if (reflexive) { DEBUG("history.find", "Marking " << price.commodity().symbol() << " as a primary commodity"); price.commodity().add_flags(COMMODITY_PRIMARY); } else { DEBUG("history.find", "Marking " << symbol() << " as a primary commodity"); add_flags(COMMODITY_PRIMARY); } DEBUG("history.find", "Adding price: " << symbol() << " for " << price << " on " << date); pool().commodity_price_history.add_price(referent(), date, price); base->price_map.clear(); // a price was added, invalid the map } void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) { pool().commodity_price_history.remove_price(referent(), commodity, date); DEBUG("history.find", "Removing price: " << symbol() << " on " << date); base->price_map.clear(); // a price was added, invalid the map } void commodity_t::map_prices(function<void(datetime_t, const amount_t&)> fn, const datetime_t& moment, const datetime_t& _oldest, bool bidirectionally) { datetime_t when; if (! moment.is_not_a_date_time()) when = moment; else if (epoch) when = *epoch; else when = CURRENT_TIME(); pool().commodity_price_history.map_prices(fn, referent(), when, _oldest, bidirectionally); } optional<price_point_t> commodity_t::find_price_from_expr(expr_t& expr, const commodity_t * commodity, const datetime_t& moment) const { #if DEBUG_ON if (SHOW_DEBUG("commodity.price.find")) { ledger::_log_buffer << "valuation expr: "; expr.dump(ledger::_log_buffer); DEBUG("commodity.price.find", ""); } #endif value_t result(expr.calc(*scope_t::default_scope)); if (is_expr(result)) { value_t call_args; call_args.push_back(string_value(base_symbol())); call_args.push_back(moment); if (commodity) call_args.push_back(string_value(commodity->symbol())); result = as_expr(result)->call(call_args, *scope_t::default_scope); } return price_point_t(moment, result.to_amount()); } optional<price_point_t> commodity_t::find_price(const commodity_t * commodity, const datetime_t& moment, const datetime_t& oldest) const { DEBUG("commodity.price.find", "commodity_t::find_price(" << symbol() << ")"); const commodity_t * target = NULL; if (commodity) target = commodity; else if (pool().default_commodity) target = &*pool().default_commodity; if (target && this == target) return none; base_t::memoized_price_entry entry(moment, oldest, commodity ? commodity : NULL); DEBUG("commodity.price.find", "looking for memoized args: " << (! moment.is_not_a_date_time() ? format_datetime(moment) : "NONE") << ", " << (! oldest.is_not_a_date_time() ? format_datetime(oldest) : "NONE") << ", " << (commodity ? commodity->symbol() : "NONE")); { base_t::memoized_price_map::iterator i = base->price_map.find(entry); if (i != base->price_map.end()) { DEBUG("commodity.price.find", "found! returning: " << ((*i).second ? (*i).second->price : amount_t(0L))); return (*i).second; } } datetime_t when; if (! moment.is_not_a_date_time()) when = moment; else if (epoch) when = *epoch; else when = CURRENT_TIME(); if (base->value_expr) return find_price_from_expr(*base->value_expr, commodity, when); optional<price_point_t> point(target ? pool().commodity_price_history.find_price(referent(), *target, when, oldest) : pool().commodity_price_history.find_price(referent(), when, oldest)); // Record this price point in the memoization map if (base->price_map.size() > base_t::max_price_map_size) { DEBUG("history.find", "price map has grown too large, clearing it by half"); for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) base->price_map.erase(base->price_map.begin()); } DEBUG("history.find", "remembered: " << (point ? point->price : amount_t(0L))); base->price_map.insert(base_t::memoized_price_map::value_type(entry, point)); return point; } optional<price_point_t> commodity_t::check_for_updated_price(const optional<price_point_t>& point, const datetime_t& moment, const commodity_t* in_terms_of) { if (pool().get_quotes && ! has_flags(COMMODITY_NOMARKET)) { bool exceeds_leeway = true; if (point) { time_duration_t::sec_type seconds_diff; if (! moment.is_not_a_date_time()) { seconds_diff = (moment - point->when).total_seconds(); DEBUG("commodity.download", "moment = " << moment); DEBUG("commodity.download", "slip.moment = " << seconds_diff); } else { seconds_diff = (TRUE_CURRENT_TIME() - point->when).total_seconds(); DEBUG("commodity.download", "slip.now = " << seconds_diff); } DEBUG("commodity.download", "leeway = " << pool().quote_leeway); if (seconds_diff < pool().quote_leeway) exceeds_leeway = false; } if (exceeds_leeway) { DEBUG("commodity.download", "attempting to download a more current quote..."); if (optional<price_point_t> quote = pool().get_commodity_quote(referent(), in_terms_of)) { if (! in_terms_of || (quote->price.has_commodity() && quote->price.commodity_ptr() == in_terms_of)) return quote; } } } return point; } commodity_t& commodity_t::nail_down(const expr_t& expr) { annotation_t new_details; new_details.value_expr = expr; new_details.add_flags(ANNOTATION_VALUE_EXPR_CALCULATED); return *pool().find_or_create(symbol(), new_details); } commodity_t::operator bool() const { return this != pool().null_commodity; } namespace { // Invalid commodity characters: // SPACE, TAB, NEWLINE, RETURN // 0-9 . , ; : ? ! - + * / ^ & | = // < > { } [ ] ( ) @ static int invalid_chars[256] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, /* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; bool is_reserved_token(const char * buf) { switch (buf[0]) { case 'a': return std::strcmp(buf, "and") == 0; case 'd': return std::strcmp(buf, "div") == 0; case 'e': return std::strcmp(buf, "else") == 0; case 'f': return std::strcmp(buf, "false") == 0; case 'i': return std::strcmp(buf, "if") == 0; case 'o': return std::strcmp(buf, "or") == 0; case 'n': return std::strcmp(buf, "not") == 0; case 't': return std::strcmp(buf, "true") == 0; } return false; } } bool commodity_t::symbol_needs_quotes(const string& symbol) { foreach (char ch, symbol) if (invalid_chars[static_cast<unsigned char>(ch)]) return true; return false; } void commodity_t::parse_symbol(std::istream& in, string& symbol) { std::istream::pos_type pos = in.tellg(); char buf[256]; int c = peek_next_nonws(in); if (c == '"') { in.get(); READ_INTO(in, buf, 255, c, c != '"'); if (c == '"') in.get(); else throw_(amount_error, _("Quoted commodity symbol lacks closing quote")); } else { char * _p = buf; while (_p - buf < 255 && in.good() && ! in.eof() && ! invalid_chars[c]) { c = in.get(); if (c == '\\') { c = in.get(); if (in.eof()) throw_(amount_error, _("Backslash at end of commodity name")); } *_p++ = c; c = in.peek(); } *_p = '\0'; if (is_reserved_token(buf)) buf[0] = '\0'; } symbol = buf; if (symbol.length() == 0) { in.clear(); in.seekg(pos, std::ios::beg); } } void commodity_t::parse_symbol(char *& p, string& symbol) { if (*p == '"') { char * q = std::strchr(p + 1, '"'); if (! q) throw_(amount_error, _("Quoted commodity symbol lacks closing quote")); symbol = string(p + 1, 0, static_cast<std::string::size_type>(q - p - 1)); p = q + 2; } else { char * q = next_element(p); symbol = p; if (q) p = q; else p += symbol.length(); } if (symbol.empty()) throw_(amount_error, _("Failed to parse commodity")); } void commodity_t::print(std::ostream& out, bool elide_quotes, bool) const { string sym = symbol(); if (elide_quotes && has_flags(COMMODITY_STYLE_SEPARATED) && ! sym.empty() && sym[0] == '"' && ! std::strchr(sym.c_str(), ' ')) { string subsym(sym, 1, sym.length() - 2); if (! all(subsym, is_digit())) out << subsym; else out << sym; } else out << sym; } bool commodity_t::valid() const { if (symbol().empty() && this != pool().null_commodity) { DEBUG("ledger.validate", "commodity_t: symbol().empty() && this != null_commodity"); return false; } if (annotated && ! base) { DEBUG("ledger.validate", "commodity_t: annotated && ! base"); return false; } if (precision() > 16) { DEBUG("ledger.validate", "commodity_t: precision() > 16"); return false; } return true; } int commodity_t::compare_by_commodity::operator()(const amount_t * left, const amount_t * right) const { commodity_t& leftcomm(left->commodity()); commodity_t& rightcomm(right->commodity()); DEBUG("commodity.compare", " left symbol (" << leftcomm << ")"); DEBUG("commodity.compare", "right symbol (" << rightcomm << ")"); int cmp = leftcomm.base_symbol().compare(rightcomm.base_symbol()); if (cmp != 0) { DEBUG("commodity.compare", "symbol is <"); return cmp; } if (! leftcomm.has_annotation() && rightcomm.has_annotation()) { DEBUG("commodity.compare", "left has no annotation, right does"); return -1; } else if (leftcomm.has_annotation() && ! rightcomm.has_annotation()) { DEBUG("commodity.compare", "right has no annotation, left does"); return 1; } else if (! leftcomm.has_annotation() && ! rightcomm.has_annotation()) { DEBUG("commodity.compare", "there are no annotations, commodities match"); return 0; } annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm)); annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm)); if (! aleftcomm.details.price && arightcomm.details.price) { DEBUG("commodity.compare", "left has no price, right does"); return -1; } if (aleftcomm.details.price && ! arightcomm.details.price) { DEBUG("commodity.compare", "right has no price, left does"); return 1; } if (aleftcomm.details.price && arightcomm.details.price) { amount_t leftprice(*aleftcomm.details.price); amount_t rightprice(*arightcomm.details.price); if (leftprice.commodity() != rightprice.commodity()) { // Since we have two different amounts, there's really no way to // establish a true sorting order; we'll just do it based on the // numerical values, before falling back to comparing the prices // with their original commodity. amount_t leftpricenumeric(leftprice); amount_t rightpricenumeric(rightprice); leftpricenumeric.clear_commodity(); rightpricenumeric.clear_commodity(); DEBUG("commodity.compare", "both have price, commodities don't match, recursing"); int cmp2 = commodity_t::compare_by_commodity()(&leftpricenumeric, &rightpricenumeric); if (cmp2 != 0) { DEBUG("commodity.compare", "recursion found a disparity"); return cmp2; } DEBUG("commodity.compare", "recursion found no difference, comparing prices with commodity"); return commodity_t::compare_by_commodity()(&leftprice, &rightprice); } else { if (leftprice < rightprice) { DEBUG("commodity.compare", "left price is less"); return -1; } else if (leftprice > rightprice) { DEBUG("commodity.compare", "left price is more"); return 1; } } } if (! aleftcomm.details.date && arightcomm.details.date) { DEBUG("commodity.compare", "left has no date, right does"); return -1; } if (aleftcomm.details.date && ! arightcomm.details.date) { DEBUG("commodity.compare", "right has no date, left does"); return 1; } if (aleftcomm.details.date && arightcomm.details.date) { gregorian::date_duration diff = *aleftcomm.details.date - *arightcomm.details.date; DEBUG("commodity.compare", "both have dates, comparing on difference"); if (diff.is_negative()) { DEBUG("commodity.compare", "dates differ"); return -1; } gregorian::date_duration diff2 = *arightcomm.details.date - *aleftcomm.details.date; if (diff2.is_negative()) { DEBUG("commodity.compare", "dates differ"); return 1; } } if (! aleftcomm.details.tag && arightcomm.details.tag) { DEBUG("commodity.compare", "left has no tag, right does"); return -1; } if (aleftcomm.details.tag && ! arightcomm.details.tag) { DEBUG("commodity.compare", "right has no tag, left does"); return 1; } if (aleftcomm.details.tag && arightcomm.details.tag) { DEBUG("commodity.compare", "both have tags, comparing lexically"); if (*aleftcomm.details.tag < *arightcomm.details.tag) return -1; else if (*aleftcomm.details.tag > *arightcomm.details.tag) return 1; } if (! aleftcomm.details.value_expr && arightcomm.details.value_expr) { DEBUG("commodity.compare", "left has no value expr, right does"); return -1; } if (aleftcomm.details.value_expr && ! arightcomm.details.value_expr) { DEBUG("commodity.compare", "right has no value expr, left does"); return 1; } if (aleftcomm.details.value_expr && arightcomm.details.value_expr) { DEBUG("commodity.compare", "both have value exprs, comparing text reprs"); return (aleftcomm.details.value_expr->text() < arightcomm.details.value_expr->text()); } DEBUG("commodity.compare", "the two are incomparable, which should never happen"); assert(false); return -1; } void put_commodity(property_tree::ptree& st, const commodity_t& comm, bool commodity_details) { std::string flags; if (! (comm.has_flags(COMMODITY_STYLE_SUFFIXED))) flags += 'P'; if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) flags += 'S'; if (comm.has_flags(COMMODITY_STYLE_THOUSANDS)) flags += 'T'; if (comm.has_flags(COMMODITY_STYLE_DECIMAL_COMMA)) flags += 'D'; st.put("<xmlattr>.flags", flags); st.put("symbol", comm.symbol()); if (commodity_details && comm.has_annotation()) put_annotation(st.put("annotation", ""), as_annotated_commodity(comm).details); } } // namespace ledger �������������������������������������������������������������������������������������������ledger-3.3.2/src/commodity.h������������������������������������������������������������������������0000664�0000000�0000000�00000021651�14411236400�0015725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup math */ /** * @file commodity.h * @author John Wiegley * * @ingroup math * * @brief Types for handling commodities * * This file contains one of the most basic types in Ledger: * commodity_t, and its annotated cousin, annotated_commodity_t. */ #ifndef INCLUDED_COMMODITY_H #define INCLUDED_COMMODITY_H #include "expr.h" namespace ledger { struct keep_details_t; class commodity_pool_t; DECLARE_EXCEPTION(commodity_error, std::runtime_error); struct price_point_t { datetime_t when; amount_t price; price_point_t() {} price_point_t(datetime_t _when, amount_t _price) : when(_when), price(_price) {} bool operator==(const price_point_t& other) const { return when == other.when && price == other.price; } }; class commodity_t : public delegates_flags<uint_least16_t>, public equality_comparable1<commodity_t, noncopyable> { protected: friend class commodity_pool_t; friend class annotated_commodity_t; class base_t : public noncopyable, public supports_flags<uint_least16_t> { public: #define COMMODITY_STYLE_DEFAULTS 0x000 #define COMMODITY_STYLE_SUFFIXED 0x001 #define COMMODITY_STYLE_SEPARATED 0x002 #define COMMODITY_STYLE_DECIMAL_COMMA 0x004 #define COMMODITY_STYLE_THOUSANDS 0x008 #define COMMODITY_NOMARKET 0x010 #define COMMODITY_BUILTIN 0x020 #define COMMODITY_WALKED 0x040 #define COMMODITY_KNOWN 0x080 #define COMMODITY_PRIMARY 0x100 #define COMMODITY_SAW_ANNOTATED 0x200 #define COMMODITY_SAW_ANN_PRICE_FLOAT 0x400 #define COMMODITY_SAW_ANN_PRICE_FIXATED 0x800 #define COMMODITY_STYLE_TIME_COLON 0x1000 #define COMMODITY_STYLE_NO_MIGRATE 0x2000 string symbol; optional<std::size_t> graph_index; amount_t::precision_t precision; optional<string> name; optional<string> note; optional<amount_t> smaller; optional<amount_t> larger; optional<expr_t> value_expr; typedef tuple<datetime_t, datetime_t, const commodity_t *> memoized_price_entry; typedef std::map<memoized_price_entry, optional<price_point_t> > memoized_price_map; static const std::size_t max_price_map_size = 8; mutable memoized_price_map price_map; public: explicit base_t(const string& _symbol) : supports_flags<uint_least16_t> (commodity_t::decimal_comma_by_default ? static_cast<uint_least16_t>(COMMODITY_STYLE_DECIMAL_COMMA) : static_cast<uint_least16_t>(COMMODITY_STYLE_DEFAULTS)), symbol(_symbol), precision(0) { TRACE_CTOR(commodity_t::base_t, "const string&"); } virtual ~base_t() { TRACE_DTOR(commodity_t::base_t); } }; shared_ptr<base_t> base; commodity_pool_t * parent_; optional<string> qualified_symbol; bool annotated; explicit commodity_t(commodity_pool_t * _parent, const shared_ptr<base_t>& _base) : delegates_flags<uint_least16_t>(*_base.get()), base(_base), parent_(_parent), annotated(false) { TRACE_CTOR(commodity_t, "commodity_pool_t *, shared_ptr<base_t>"); } public: static bool decimal_comma_by_default; static bool time_colon_by_default; virtual ~commodity_t() { TRACE_DTOR(commodity_t); } operator bool() const; virtual bool operator==(const commodity_t& comm) const { if (comm.annotated) return comm == *this; return base.get() == comm.base.get(); } bool operator==(const string& name) const { return base_symbol() == name; } static bool symbol_needs_quotes(const string& symbol); virtual commodity_t& referent() { return *this; } virtual const commodity_t& referent() const { return *this; } bool has_annotation() const { return annotated; } virtual commodity_t& strip_annotations(const keep_details_t&) { return *this; } virtual void write_annotations(std::ostream&, bool) const {} commodity_pool_t& pool() const { return *parent_; } string base_symbol() const { return base->symbol; } string symbol() const { return qualified_symbol ? *qualified_symbol : base_symbol(); } optional<std::size_t> graph_index() const {; return base->graph_index; } void set_graph_index(const optional<std::size_t>& arg = none) { base->graph_index = arg; } optional<string> name() const { return base->name; } void set_name(const optional<string>& arg = none) { base->name = arg; } optional<string> note() const { return base->note; } void set_note(const optional<string>& arg = none) { base->note = arg; } amount_t::precision_t precision() const { return base->precision; } void set_precision(amount_t::precision_t arg) { base->precision = arg; } optional<amount_t> smaller() const { return base->smaller; } void set_smaller(const optional<amount_t>& arg = none) { base->smaller = arg; } optional<amount_t> larger() const { return base->larger; } void set_larger(const optional<amount_t>& arg = none) { base->larger = arg; } virtual optional<expr_t> value_expr() const { return base->value_expr; } void set_value_expr(const optional<expr_t>& expr = none) { base->value_expr = expr; } void add_price(const datetime_t& date, const amount_t& price, const bool reflexive = true); void remove_price(const datetime_t& date, commodity_t& commodity); void map_prices(function<void(datetime_t, const amount_t&)> fn, const datetime_t& moment = datetime_t(), const datetime_t& _oldest = datetime_t(), bool bidirectionally = false); optional<price_point_t> find_price_from_expr(expr_t& expr, const commodity_t * commodity, const datetime_t& moment) const; optional<price_point_t> virtual find_price(const commodity_t * commodity = NULL, const datetime_t& moment = datetime_t(), const datetime_t& oldest = datetime_t()) const; optional<price_point_t> check_for_updated_price(const optional<price_point_t>& point, const datetime_t& moment, const commodity_t * in_terms_of); commodity_t& nail_down(const expr_t& expr); // Methods related to parsing, reading, writing, etc., the commodity // itself. static void parse_symbol(std::istream& in, string& symbol); static void parse_symbol(char *& p, string& symbol); static string parse_symbol(std::istream& in) { string temp; parse_symbol(in, temp); return temp; } virtual void print(std::ostream& out, bool elide_quotes = false, bool print_annotations = false) const; bool valid() const; struct compare_by_commodity { int operator()(const amount_t * left, const amount_t * right) const; }; }; inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) { comm.print(out, false, true); return out; } void put_commodity(property_tree::ptree& pt, const commodity_t& comm, bool commodity_details = false); //simple struct to allow std::map to compare commodities names struct commodity_compare { bool operator() (const commodity_t* lhs, const commodity_t* rhs) const { return (lhs->symbol().compare(rhs->symbol()) < 0); } }; } // namespace ledger #endif // INCLUDED_COMMODITY_H ���������������������������������������������������������������������������������������ledger-3.3.2/src/compare.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000011610�14411236400�0015477�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "compare.h" #include "op.h" #include "scope.h" #include "post.h" #include "account.h" #include "report.h" namespace ledger { void push_sort_value(std::list<sort_value_t>& sort_values, expr_t::ptr_op_t node, scope_t& scope) { if (node->kind == expr_t::op_t::O_CONS) { while (node && node->kind == expr_t::op_t::O_CONS) { push_sort_value(sort_values, node->left(), scope); node = node->has_right() ? node->right() : NULL; } } else { bool inverted = false; if (node->kind == expr_t::op_t::O_NEG) { inverted = true; node = node->left(); } sort_values.push_back(sort_value_t()); sort_values.back().inverted = inverted; sort_values.back().value = expr_t(node).calc(scope).simplified(); if (sort_values.back().value.is_null()) throw_(calc_error, _("Could not determine sorting value based an expression")); } } template <> void compare_items<post_t>::find_sort_values( std::list<sort_value_t>& sort_values, scope_t& scope) { bind_scope_t bound_scope(report, scope); push_sort_value(sort_values, sort_order.get_op(), bound_scope); } template <> void compare_items<account_t>::find_sort_values( std::list<sort_value_t>& sort_values, scope_t& scope) { bind_scope_t bound_scope(report, scope); push_sort_value(sort_values, sort_order.get_op(), bound_scope); } template <> bool compare_items<post_t>::operator()(post_t * left, post_t * right) { assert(left); assert(right); post_t::xdata_t& lxdata(left->xdata()); if (! lxdata.has_flags(POST_EXT_SORT_CALC)) { if (sort_order.get_context()) { bind_scope_t bound_scope(*sort_order.get_context(), *left); find_sort_values(lxdata.sort_values, bound_scope); } else { find_sort_values(lxdata.sort_values, *left); } lxdata.add_flags(POST_EXT_SORT_CALC); } post_t::xdata_t& rxdata(right->xdata()); if (! rxdata.has_flags(POST_EXT_SORT_CALC)) { if (sort_order.get_context()) { bind_scope_t bound_scope(*sort_order.get_context(), *right); find_sort_values(rxdata.sort_values, bound_scope); } else { find_sort_values(rxdata.sort_values, *right); } rxdata.add_flags(POST_EXT_SORT_CALC); } return sort_value_is_less_than(lxdata.sort_values, rxdata.sort_values); } template <> bool compare_items<account_t>::operator()(account_t * left, account_t * right) { assert(left); assert(right); account_t::xdata_t& lxdata(left->xdata()); if (! lxdata.has_flags(ACCOUNT_EXT_SORT_CALC)) { if (sort_order.get_context()) { bind_scope_t bound_scope(*sort_order.get_context(), *left); find_sort_values(lxdata.sort_values, bound_scope); } else { find_sort_values(lxdata.sort_values, *left); } lxdata.add_flags(ACCOUNT_EXT_SORT_CALC); } account_t::xdata_t& rxdata(right->xdata()); if (! rxdata.has_flags(ACCOUNT_EXT_SORT_CALC)) { if (sort_order.get_context()) { bind_scope_t bound_scope(*sort_order.get_context(), *right); find_sort_values(rxdata.sort_values, bound_scope); } else { find_sort_values(rxdata.sort_values, *right); } rxdata.add_flags(ACCOUNT_EXT_SORT_CALC); } DEBUG("value.sort", "Comparing accounts " << left->fullname() << " <> " << right->fullname()); return sort_value_is_less_than(lxdata.sort_values, rxdata.sort_values); } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/compare.h��������������������������������������������������������������������������0000664�0000000�0000000�00000006070�14411236400�0015345�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file compare.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_COMPARE_H #define INCLUDED_COMPARE_H #include "expr.h" namespace ledger { class post_t; class account_t; class report_t; void push_sort_value(std::list<sort_value_t>& sort_values, expr_t::ptr_op_t node, scope_t& scope); template <typename T> class compare_items { expr_t sort_order; report_t& report; compare_items(); public: compare_items(const expr_t& _sort_order, report_t& _report) : sort_order(_sort_order), report(_report) { TRACE_CTOR(compare_items, "const value_expr&, report_t&"); } compare_items(const compare_items& other) : sort_order(other.sort_order), report(other.report) { TRACE_CTOR(compare_items, "copy"); } ~compare_items() throw() { TRACE_DTOR(compare_items); } void find_sort_values(std::list<sort_value_t>& sort_values, scope_t& scope); bool operator()(T * left, T * right); }; sort_value_t calc_sort_value(const expr_t::ptr_op_t op); template <typename T> bool compare_items<T>::operator()(T * left, T * right) { assert(left); assert(right); return sort_value_is_less_than(find_sort_values(left), find_sort_values(right)); } template <> bool compare_items<post_t>::operator()(post_t * left, post_t * right); template <> bool compare_items<account_t>::operator()(account_t * left, account_t * right); } // namespace ledger #endif // INCLUDED_COMPARE_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/context.h��������������������������������������������������������������������������0000664�0000000�0000000�00000012057�14411236400�0015405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file context.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_CONTEXT_H #define INCLUDED_CONTEXT_H #include "utils.h" #include "times.h" #if HAVE_GPGME #include "gpgme.h" #endif namespace ledger { class journal_t; class account_t; class scope_t; class parse_context_t { public: static const std::size_t MAX_LINE = 4096; shared_ptr<std::istream> stream; path pathname; path current_directory; journal_t * journal; account_t * master; scope_t * scope; char linebuf[MAX_LINE + 1]; std::istream::pos_type line_beg_pos; std::istream::pos_type curr_pos; std::size_t linenum; std::size_t errors; std::size_t count; std::size_t sequence; std::string last; explicit parse_context_t(const path& cwd) : current_directory(cwd), master(NULL), scope(NULL), linenum(0), errors(0), count(0), sequence(1) {} explicit parse_context_t(shared_ptr<std::istream> _stream, const path& cwd) : stream(_stream), current_directory(cwd), master(NULL), scope(NULL), linenum(0), errors(0), count(0), sequence(1) {} parse_context_t(const parse_context_t& context) : stream(context.stream), pathname(context.pathname), current_directory(context.current_directory), journal(context.journal), master(context.master), scope(context.scope), line_beg_pos(context.line_beg_pos), curr_pos(context.curr_pos), linenum(context.linenum), errors(context.errors), count(context.count), sequence(context.sequence) { std::memcpy(linebuf, context.linebuf, MAX_LINE); } string location() const { return file_context(pathname, linenum); } void warning(const string& what) const { warning_func(location() + " " + what); } void warning(const boost::format& what) const { warning_func(location() + " " + string(what.str())); } }; inline parse_context_t open_for_reading(const path& pathname, const path& cwd) { path filename = resolve_path(pathname); filename = filesystem::absolute(filename, cwd); if (! exists(filename) || is_directory(filename)) throw_(std::runtime_error, _f("Cannot read journal file %1%") % filename); path parent(filename.parent_path()); #if HAVE_GPGME shared_ptr<std::istream> stream(decrypted_stream_t::open_stream(filename)); #else shared_ptr<std::istream> stream(new ifstream(filename)); #endif parse_context_t context(stream, parent); context.pathname = filename; return context; } class parse_context_stack_t { std::list<parse_context_t> parsing_context; public: void push() { parsing_context.push_front(parse_context_t(filesystem::current_path())); } void push(shared_ptr<std::istream> stream, const path& cwd = filesystem::current_path()) { parsing_context.push_front(parse_context_t(stream, cwd)); } void push(const path& pathname, const path& cwd = filesystem::current_path()) { parsing_context.push_front(open_for_reading(pathname, cwd)); } void push(const parse_context_t& context) { parsing_context.push_front(context); } void pop() { assert(! parsing_context.empty()); parsing_context.pop_front(); } parse_context_t& get_current() { assert(! parsing_context.empty()); return parsing_context.front(); } }; } // namespace ledger #endif // INCLUDED_CONTEXT_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/convert.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000011425�14411236400�0015535�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "convert.h" #include "csv.h" #include "scope.h" #include "iterators.h" #include "report.h" #include "xact.h" #include "print.h" #include "lookup.h" namespace ledger { value_t convert_command(call_scope_t& args) { report_t& report(args.context<report_t>()); journal_t& journal(*report.session.journal.get()); string bucket_name; if (report.HANDLED(account_)) bucket_name = report.HANDLER(account_).str(); else bucket_name = _("Equity:Unknown"); account_t * bucket = journal.master->find_account(bucket_name); account_t * unknown = journal.master->find_account(_("Expenses:Unknown")); // Create a flat list xacts_list current_xacts(journal.xacts_begin(), journal.xacts_end()); // Read in the series of transactions from the CSV file print_xacts formatter(report); path csv_file_path(args.get<string>(0)); report.session.parsing_context.push(csv_file_path); parse_context_t& context(report.session.parsing_context.get_current()); context.journal = &journal; context.master = bucket; csv_reader reader(context); try { while (xact_t * xact = reader.read_xact(report.HANDLED(rich_data))) { if (report.HANDLED(invert)) { foreach (post_t * post, xact->posts) post->amount.in_place_negate(); } string ref = (xact->has_tag(_("UUID")) ? xact->get_tag(_("UUID"))->to_string() : sha1sum(reader.get_last_line())); checksum_map_t::const_iterator entry = journal.checksum_map.find(ref); if (entry != journal.checksum_map.end()) { INFO(file_context(reader.get_pathname(), reader.get_linenum()) << " " << "Ignoring known UUID " << ref); checked_delete(xact); // ignore it continue; } if (report.HANDLED(rich_data) && ! xact->has_tag(_("UUID"))) xact->set_tag(_("UUID"), string_value(ref)); if (xact->posts.front()->account == NULL) { if (account_t * acct = (report.HANDLED(auto_match) ? lookup_probable_account(xact->payee, current_xacts.rbegin(), current_xacts.rend(), bucket).second : NULL)) xact->posts.front()->account = acct; else xact->posts.front()->account = unknown; } if (! journal.add_xact(xact)) { checked_delete(xact); throw_(std::runtime_error, _("Failed to finalize derived transaction (check commodities)")); } else { xact_posts_iterator xact_iter(*xact); while (post_t * post = *xact_iter++) formatter(*post); } } formatter.flush(); } catch (const std::exception&) { add_error_context(_f("While parsing file %1%") % file_context(reader.get_pathname(), reader.get_linenum())); add_error_context(_("While parsing CSV line:")); add_error_context(line_context(reader.get_last_line())); throw; } // If not, transform the payee according to regexps // Set the account to a default value, then transform the account according // to the payee // Print out the final form of the transaction return true; } } // namespace ledger �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/convert.h��������������������������������������������������������������������������0000664�0000000�0000000�00000003550�14411236400�0015377�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file convert.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_CONVERT_H #define INCLUDED_CONVERT_H #include "value.h" namespace ledger { class call_scope_t; value_t convert_command(call_scope_t& scope); } // namespace ledger #endif // INCLUDED_CONVERT_H ��������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/csv.cc�����������������������������������������������������������������������������0000664�0000000�0000000�00000016614�14411236400�0014655�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "csv.h" #include "xact.h" #include "post.h" #include "account.h" #include "journal.h" #include "pool.h" namespace ledger { string csv_reader::read_field(std::istream& in) { string field; char c; if (in.peek() == '"' || in.peek() == '|') { in.get(c); char x; while (in.good() && ! in.eof()) { in.get(x); if (x == '\\') { in.get(x); } else if (x == '"' && in.peek() == '"') { in.get(x); } else if (x == c) { if (x == '|') in.unget(); else if (in.peek() == ',') in.get(c); break; } if (x != '\0') field += x; } } else { while (in.good() && ! in.eof()) { in.get(c); if (in.good()) { if (c == ',') break; if (c != '\0') field += c; } } } trim(field); return field; } char * csv_reader::next_line(std::istream& in) { while (in.good() && ! in.eof() && in.peek() == '#') in.getline(context.linebuf, parse_context_t::MAX_LINE); if (! in.good() || in.eof() || in.peek() == -1) return NULL; in.getline(context.linebuf, parse_context_t::MAX_LINE); return context.linebuf; } void csv_reader::read_index(std::istream& in) { char * line = next_line(in); if (! line) return; std::istringstream instr(line); while (instr.good() && ! instr.eof()) { string field = read_field(instr); names.push_back(field); DEBUG("csv.parse", "Header field: " << field); for (auto& mask : masks) { if (mask.first.match(field)) { index.push_back(mask.second); break; } } } } xact_t * csv_reader::read_xact(bool rich_data) { char * line = next_line(*context.stream.get()); if (! line || index.empty()) return NULL; context.linenum++; std::istringstream instr(line); unique_ptr<xact_t> xact(new xact_t); unique_ptr<post_t> post(new post_t); xact->set_state(item_t::CLEARED); xact->pos = position_t(); xact->pos->pathname = context.pathname; xact->pos->beg_pos = context.stream->tellg(); xact->pos->beg_line = context.linenum; xact->pos->sequence = context.sequence++; post->xact = xact.get(); post->pos = position_t(); post->pos->pathname = context.pathname; post->pos->beg_pos = context.stream->tellg(); post->pos->beg_line = context.linenum; post->pos->sequence = context.sequence++; post->set_state(item_t::CLEARED); post->account = NULL; std::vector<int>::size_type n = 0; amount_t amt; string total; string field; while (instr.good() && ! instr.eof() && n < index.size()) { field = read_field(instr); switch (index[n]) { case FIELD_DATE: xact->_date = parse_date(field); break; case FIELD_DATE_AUX: if (! field.empty()) xact->_date_aux = parse_date(field); break; case FIELD_CODE: if (! field.empty()) xact->code = field; break; case FIELD_PAYEE: { bool found = false; foreach (payee_alias_mapping_t& value, context.journal->payee_alias_mappings) { DEBUG("csv.mappings", "Looking for payee mapping: " << value.first); if (value.first.match(field)) { xact->payee = value.second; found = true; break; } } if (! found) xact->payee = field; break; } case FIELD_DEBIT: case FIELD_CREDIT: { if (field.length() == 0) break; std::istringstream amount_str(field); amt.parse(amount_str, PARSE_NO_REDUCE); if (! amt.has_commodity() && commodity_pool_t::current_pool->default_commodity) amt.set_commodity(*commodity_pool_t::current_pool->default_commodity); if (index[n] == FIELD_DEBIT) amt = -amt; if (!post->amount.is_null()) throw_(csv_error, _("Cannot have two values for a single transaction")); post->amount = amt; break; } case FIELD_COST: { std::istringstream amount_str(field); amt.parse(amount_str, PARSE_NO_REDUCE); if (! amt.has_commodity() && commodity_pool_t::current_pool->default_commodity) amt.set_commodity(*commodity_pool_t::current_pool->default_commodity); post->cost = amt; break; } case FIELD_TOTAL: total = field; break; case FIELD_NOTE: if (! field.empty()) xact->note = field; break; case FIELD_UNKNOWN: if (! names[n].empty() && ! field.empty()) xact->set_tag(names[n], string_value(field)); break; } n++; } if (rich_data) { xact->set_tag(_("Imported"), string_value(format_date(CURRENT_DATE(), FMT_WRITTEN))); xact->set_tag(_("CSV"), string_value(line)); } // Translate the account name, if we have enough information to do so foreach (account_mapping_t& value, context.journal->payees_for_unknown_accounts) { if (value.first.match(xact->payee)) { post->account = value.second; break; } } xact->add_post(post.release()); // Create the "balancing post", which refers to the account for this data post.reset(new post_t); post->xact = xact.get(); post->pos = position_t(); post->pos->pathname = context.pathname; post->pos->beg_pos = context.stream->tellg(); post->pos->beg_line = context.linenum; post->pos->sequence = context.sequence++; post->set_state(item_t::CLEARED); post->account = context.master; if (! amt.is_null()) post->amount = - amt; if (! total.empty()) { std::istringstream assigned_amount_str(total); amt.parse(assigned_amount_str, PARSE_NO_REDUCE); if (! amt.has_commodity() && commodity_pool_t::current_pool->default_commodity) amt.set_commodity(*commodity_pool_t::current_pool->default_commodity); post->assigned_amount = amt; } xact->add_post(post.release()); return xact.release(); } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/csv.h������������������������������������������������������������������������������0000664�0000000�0000000�00000006656�14411236400�0014524�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file csv.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_CSV_H #define INCLUDED_CSV_H #include "value.h" #include "context.h" namespace ledger { class xact_t; class journal_t; class account_t; DECLARE_EXCEPTION(csv_error, std::runtime_error); class csv_reader { parse_context_t context; enum headers_t { FIELD_DATE = 0, FIELD_DATE_AUX, FIELD_CODE, FIELD_PAYEE, FIELD_CREDIT, FIELD_DEBIT, FIELD_COST, FIELD_TOTAL, FIELD_NOTE, FIELD_UNKNOWN }; std::array<std::pair<mask_t, headers_t>, 10> masks; std::vector<headers_t> index; std::vector<string> names; public: csv_reader(parse_context_t& _context) : context(_context), masks{ std::make_pair(mask_t("date"), FIELD_DATE), std::make_pair(mask_t("posted( ?date)?"), FIELD_DATE_AUX), std::make_pair(mask_t("code"), FIELD_CODE), std::make_pair(mask_t("(payee|desc(ription)?|title)"), FIELD_PAYEE), std::make_pair(mask_t("credit|amount"), FIELD_CREDIT), std::make_pair(mask_t("debit"), FIELD_DEBIT), std::make_pair(mask_t("cost"), FIELD_COST), std::make_pair(mask_t("total"), FIELD_TOTAL), std::make_pair(mask_t("note"), FIELD_NOTE), std::make_pair(mask_t(""), FIELD_UNKNOWN) } { read_index(*context.stream.get()); TRACE_CTOR(csv_reader, "parse_context_t&"); } ~csv_reader() { TRACE_DTOR(csv_reader); } void read_index(std::istream& in); string read_field(std::istream& in); char * next_line(std::istream& in); xact_t * read_xact(bool rich_data); const char * get_last_line() const { return context.linebuf; } path get_pathname() const { return context.pathname; } std::size_t get_linenum() const { return context.linenum; } }; } // namespace ledger #endif // INCLUDED_CSV_H ����������������������������������������������������������������������������������ledger-3.3.2/src/draft.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000041717�14411236400�0015164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "draft.h" #include "xact.h" #include "post.h" #include "account.h" #include "journal.h" #include "session.h" #include "report.h" #include "lookup.h" #include "print.h" namespace ledger { void draft_t::xact_template_t::dump(std::ostream& out) const { if (date) out << _("Date: ") << *date << std::endl; else out << _("Date: <today>") << std::endl; if (code) out << _("Code: ") << *code << std::endl; if (note) out << _("Note: ") << *note << std::endl; if (payee_mask.empty()) out << _("Payee mask: INVALID (template expression will cause an error)") << std::endl; else out << _("Payee mask: ") << payee_mask << std::endl; if (posts.empty()) { out << std::endl << _("<Posting copied from last related transaction>") << std::endl; } else { foreach (const post_template_t& post, posts) { out << std::endl << _f("[Posting \"%1%\"]") % (post.from ? _("from") : _("to")) << std::endl; if (post.account_mask) out << _(" Account mask: ") << *post.account_mask << std::endl; else if (post.from) out << _(" Account mask: <use last of last related accounts>") << std::endl; else out << _(" Account mask: <use first of last related accounts>") << std::endl; if (post.amount) out << _(" Amount: ") << *post.amount << std::endl; if (post.cost) out << _(" Cost: ") << *post.cost_operator << " " << *post.cost << std::endl; } } } void draft_t::parse_args(const value_t& args) { regex date_mask(_("([0-9]+(?:[-/.][0-9]+)?(?:[-/.][0-9]+))?")); smatch what; bool check_for_date = true; tmpl = xact_template_t(); optional<date_time::weekdays> weekday; xact_template_t::post_template_t * post = NULL; value_t::sequence_t::const_iterator begin = args.begin(); value_t::sequence_t::const_iterator end = args.end(); for (; begin != end; begin++) { if (check_for_date && regex_match((*begin).to_string(), what, date_mask)) { tmpl->date = parse_date(what[0]); check_for_date = false; } else if (check_for_date && bool(weekday = string_to_day_of_week((*begin).to_string()))) { #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif short dow = static_cast<short>(*weekday); #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 #pragma GCC diagnostic pop #endif date_t date = CURRENT_DATE() - date_duration(1); while (date.day_of_week() != dow) date -= date_duration(1); tmpl->date = date; check_for_date = false; } else { string arg = (*begin).to_string(); if (arg == "at") { if (++begin == end) throw std::runtime_error(_("Invalid xact command arguments")); tmpl->payee_mask = (*begin).to_string(); } else if (arg == "to" || arg == "from") { if (! post || post->account_mask) { tmpl->posts.push_back(xact_template_t::post_template_t()); post = &tmpl->posts.back(); } if (++begin == end) throw std::runtime_error(_("Invalid xact command arguments")); post->account_mask = mask_t((*begin).to_string()); post->from = arg == "from"; } else if (arg == "on") { if (++begin == end) throw std::runtime_error(_("Invalid xact command arguments")); tmpl->date = parse_date((*begin).to_string()); check_for_date = false; } else if (arg == "code") { if (++begin == end) throw std::runtime_error(_("Invalid xact command arguments")); tmpl->code = (*begin).to_string(); } else if (arg == "note") { if (++begin == end) throw std::runtime_error(_("Invalid xact command arguments")); tmpl->note = (*begin).to_string(); } else if (arg == "rest") { ; // just ignore this argument } else if (arg == "@" || arg == "@@") { amount_t cost; post->cost_operator = arg; if (++begin == end) throw std::runtime_error(_("Invalid xact command arguments")); arg = (*begin).to_string(); if (! cost.parse(arg, PARSE_SOFT_FAIL | PARSE_NO_MIGRATE)) throw std::runtime_error(_("Invalid xact command arguments")); post->cost = cost; } else { // Without a preposition, it is either: // // A payee, if we have not seen one // An account or an amount, if we have // An account if an amount has just been seen // An amount if an account has just been seen if (tmpl->payee_mask.empty()) { tmpl->payee_mask = arg; } else { amount_t amt; optional<mask_t> account; if (! amt.parse(arg, PARSE_SOFT_FAIL | PARSE_NO_MIGRATE)) account = mask_t(arg); if (! post || (account && post->account_mask) || (! account && post->amount)) { tmpl->posts.push_back(xact_template_t::post_template_t()); post = &tmpl->posts.back(); } if (account) { post->account_mask = account; } else { post->amount = amt; post = NULL; // an amount concludes this posting } } } } } if (! tmpl->posts.empty()) { bool has_only_from = true; bool has_only_to = true; // A single account at the end of the line is the "from" account if (tmpl->posts.size() > 1 && tmpl->posts.back().account_mask && ! tmpl->posts.back().amount) tmpl->posts.back().from = true; foreach (xact_template_t::post_template_t& post_tmpl, tmpl->posts) { if (post_tmpl.from) has_only_to = false; else has_only_from = false; } if (has_only_from) { tmpl->posts.push_front(xact_template_t::post_template_t()); } else if (has_only_to) { tmpl->posts.push_back(xact_template_t::post_template_t()); tmpl->posts.back().from = true; } } } xact_t * draft_t::insert(journal_t& journal) { if (! tmpl) return NULL; if (tmpl->payee_mask.empty()) throw std::runtime_error(_("'xact' command requires at least a payee")); xact_t * matching = NULL; unique_ptr<xact_t> added(new xact_t); if (xact_t * xact = lookup_probable_account(tmpl->payee_mask.str(), journal.xacts.rbegin(), journal.xacts.rend()).first) { DEBUG("draft.xact", "Found payee by lookup: transaction on line " << xact->pos->beg_line); matching = xact; } else { for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); j != journal.xacts.rend(); j++) { if (tmpl->payee_mask.match((*j)->payee)) { matching = *j; DEBUG("draft.xact", "Found payee match: transaction on line " << (*j)->pos->beg_line); break; } } } if (! tmpl->date) { added->_date = CURRENT_DATE(); DEBUG("draft.xact", "Setting date to current date"); } else { added->_date = tmpl->date; DEBUG("draft.xact", "Setting date to template date: " << *tmpl->date); } added->set_state(item_t::UNCLEARED); if (matching) { added->payee = matching->payee; //added->code = matching->code; //added->note = matching->note; #if DEBUG_ON DEBUG("draft.xact", "Setting payee from match: " << added->payee); //if (added->code) // DEBUG("draft.xact", "Setting code from match: " << *added->code); //if (added->note) // DEBUG("draft.xact", "Setting note from match: " << *added->note); #endif } else { added->payee = tmpl->payee_mask.str(); DEBUG("draft.xact", "Setting payee from template: " << added->payee); } if (tmpl->code) { added->code = tmpl->code; DEBUG("draft.xact", "Now setting code from template: " << *added->code); } if (tmpl->note) { added->note = tmpl->note; DEBUG("draft.xact", "Now setting note from template: " << *added->note); } if (tmpl->posts.empty()) { if (matching) { DEBUG("draft.xact", "Template had no postings, copying from match"); foreach (post_t * post, matching->posts) { added->add_post(new post_t(*post)); added->posts.back()->set_state(item_t::UNCLEARED); } } else { throw_(std::runtime_error, _f("No accounts, and no past transaction matching '%1%'") % tmpl->payee_mask); } } else { DEBUG("draft.xact", "Template had postings"); bool any_post_has_amount = false; foreach (xact_template_t::post_template_t& post, tmpl->posts) { if (post.amount) { DEBUG("draft.xact", " and at least one has an amount specified"); any_post_has_amount = true; break; } } foreach (xact_template_t::post_template_t& post, tmpl->posts) { unique_ptr<post_t> new_post; commodity_t * found_commodity = NULL; if (matching) { if (post.account_mask) { DEBUG("draft.xact", "Looking for matching posting based on account mask"); foreach (post_t * x, matching->posts) { if (post.account_mask->match(x->account->fullname())) { new_post.reset(new post_t(*x)); DEBUG("draft.xact", "Founding posting from line " << x->pos->beg_line); break; } } } else { if (post.from) { for (posts_list::reverse_iterator j = matching->posts.rbegin(); j != matching->posts.rend(); j++) { if ((*j)->must_balance()) { new_post.reset(new post_t(**j)); DEBUG("draft.xact", "Copied last real posting from matching"); break; } } } else { for (posts_list::iterator j = matching->posts.begin(); j != matching->posts.end(); j++) { if ((*j)->must_balance()) { new_post.reset(new post_t(**j)); DEBUG("draft.xact", "Copied first real posting from matching"); break; } } } } } if (! new_post.get()) { new_post.reset(new post_t); DEBUG("draft.xact", "New posting was NULL, creating a blank one"); } if (! new_post->account) { DEBUG("draft.xact", "New posting still needs an account"); if (post.account_mask) { DEBUG("draft.xact", "The template has an account mask"); account_t * acct = NULL; if (! acct) { acct = journal.find_account_re(post.account_mask->str()); #if DEBUG_ON if (acct) DEBUG("draft.xact", "Found account as a regular expression"); #endif } if (! acct) { acct = journal.find_account(post.account_mask->str()); #if DEBUG_ON if (acct) DEBUG("draft.xact", "Found (or created) account by name"); #endif } // Find out the default commodity to use by looking at the last // commodity used in that account for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); j != journal.xacts.rend(); j++) { foreach (post_t * x, (*j)->posts) { if (x->account == acct && ! x->amount.is_null()) { new_post.reset(new post_t(*x)); DEBUG("draft.xact", "Found account in journal postings, setting new posting"); break; } } } new_post->account = acct; DEBUG("draft.xact", "Set new posting's account to: " << acct->fullname()); } else { if (post.from) { new_post->account = journal.find_account(_("Liabilities:Unknown")); DEBUG("draft.xact", "Set new posting's account to: Liabilities:Unknown"); } else { new_post->account = journal.find_account(_("Expenses:Unknown")); DEBUG("draft.xact", "Set new posting's account to: Expenses:Unknown"); } } } assert(new_post->account); if (new_post.get() && ! new_post->amount.is_null()) { found_commodity = &new_post->amount.commodity(); if (any_post_has_amount) { new_post->amount = amount_t(); DEBUG("draft.xact", "New posting has an amount, but we cleared it"); } else { any_post_has_amount = true; DEBUG("draft.xact", "New posting has an amount, and we're using it"); } } if (post.amount) { new_post->amount = *post.amount; DEBUG("draft.xact", "Copied over posting amount"); if (post.from) { new_post->amount.in_place_negate(); DEBUG("draft.xact", "Negated new posting amount"); } } if (post.cost) { if (post.cost->sign() < 0) throw parse_error(_("A posting's cost may not be negative")); post.cost->in_place_unround(); if (*post.cost_operator == "@") { // For the sole case where the cost might be uncommoditized, // guarantee that the commodity of the cost after multiplication // is the same as it was before. commodity_t& cost_commodity(post.cost->commodity()); *post.cost *= new_post->amount; post.cost->set_commodity(cost_commodity); } else if (new_post->amount.sign() < 0) { new_post->cost->in_place_negate(); } new_post->cost = *post.cost; DEBUG("draft.xact", "Copied over posting cost"); } if (found_commodity && ! new_post->amount.is_null() && ! new_post->amount.has_commodity()) { new_post->amount.set_commodity(*found_commodity); DEBUG("draft.xact", "Set posting amount commodity to: " << new_post->amount.commodity()); new_post->amount = new_post->amount.rounded(); DEBUG("draft.xact", "Rounded posting amount to: " << new_post->amount); } added->add_post(new_post.release()); added->posts.back()->account->add_post(added->posts.back()); added->posts.back()->set_state(item_t::UNCLEARED); DEBUG("draft.xact", "Added new posting to derived entry"); } } if (! journal.add_xact(added.get())) throw_(std::runtime_error, _("Failed to finalize derived transaction (check commodities)")); return added.release(); } value_t template_command(call_scope_t& args) { report_t& report(find_scope<report_t>(args)); std::ostream& out(report.output_stream); out << _("--- Input arguments ---") << std::endl; args.value().dump(out); out << std::endl << std::endl; draft_t draft(args.value()); out << _("--- Transaction template ---") << std::endl; draft.dump(out); return true; } value_t xact_command(call_scope_t& args) { report_t& report(find_scope<report_t>(args)); draft_t draft(args.value()); unique_ptr<xact_t> new_xact(draft.insert(*report.session.journal.get())); if (new_xact.get()) { // Only consider actual postings for the "xact" command report.HANDLER(limit_).on("#xact", "actual"); report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact.get()); } return true; } } // namespace ledger �������������������������������������������������ledger-3.3.2/src/draft.h����������������������������������������������������������������������������0000664�0000000�0000000�00000006741�14411236400�0015024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file draft.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_DRAFT_H #define INCLUDED_DRAFT_H #include "exprbase.h" #include "value.h" namespace ledger { class journal_t; class xact_t; class draft_t : public expr_base_t<value_t> { typedef expr_base_t<value_t> base_type; struct xact_template_t { optional<date_t> date; optional<string> code; optional<string> note; mask_t payee_mask; struct post_template_t { bool from; optional<mask_t> account_mask; optional<amount_t> amount; optional<string> cost_operator; optional<amount_t> cost; post_template_t() : from(false) { TRACE_CTOR(post_template_t, ""); } ~post_template_t() throw() { TRACE_DTOR(post_template_t); } }; std::list<post_template_t> posts; xact_template_t() { TRACE_CTOR(xact_template_t, ""); } xact_template_t(const xact_template_t& other) : date(other.date), code(other.code), note(other.note), payee_mask(other.payee_mask), posts(other.posts) { TRACE_CTOR(xact_template_t, "copy"); } ~xact_template_t() throw() { TRACE_DTOR(xact_template_t); } void dump(std::ostream& out) const; }; optional<xact_template_t> tmpl; public: draft_t(const value_t& args) : base_type() { if (! args.empty()) parse_args(args); TRACE_CTOR(draft_t, "value_t"); } virtual ~draft_t() throw() { TRACE_DTOR(draft_t); } void parse_args(const value_t& args); virtual result_type real_calc(scope_t&) { assert(false); return true; } xact_t * insert(journal_t& journal); virtual void dump(std::ostream& out) const { if (tmpl) tmpl->dump(out); } }; value_t xact_command(call_scope_t& args); value_t template_command(call_scope_t& args); } // namespace ledger #endif // INCLUDED_DRAFT_H �������������������������������ledger-3.3.2/src/emacs.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000006654�14411236400�0015155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include <boost/algorithm/string.hpp> #include "emacs.h" #include "xact.h" #include "post.h" #include "account.h" namespace ledger { void format_emacs_posts::write_xact(xact_t& xact) { if (xact.pos) out << "\"" << escape_string(xact.pos->pathname.string()) << "\" " << xact.pos->beg_line << " "; else out << "\"\" " << -1 << " "; tm when = gregorian::to_tm(xact.date()); std::time_t date = std::mktime(&when); out << "(" << (date / 65536) << " " << (date % 65536) << " 0) "; if (xact.code) out << "\"" << escape_string(*xact.code) << "\" "; else out << "nil "; if (xact.payee.empty()) out << "nil"; else out << "\"" << escape_string(xact.payee) << "\""; out << "\n"; } void format_emacs_posts::operator()(post_t& post) { if (! post.has_xdata() || ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { if (! last_xact) { out << "(("; write_xact(*post.xact); } else if (post.xact != last_xact) { out << ")\n ("; write_xact(*post.xact); } else { out << "\n"; } if (post.pos) out << " (" << post.pos->beg_line << " "; else out << " (" << -1 << " "; out << "\"" << escape_string(post.reported_account()->fullname()) << "\" \"" << escape_string(post.amount) << "\""; switch (post.state()) { case item_t::UNCLEARED: out << " nil"; break; case item_t::CLEARED: out << " t"; break; case item_t::PENDING: out << " pending"; break; } if (post.cost) out << " \"" << escape_string(*post.cost) << "\""; if (post.note) out << " \"" << escape_string(*post.note) << "\""; out << ")"; last_xact = post.xact; post.xdata().add_flags(POST_EXT_DISPLAYED); } } string format_emacs_posts::escape_string(string raw){ replace_all(raw, "\\", "\\\\"); replace_all(raw, "\"", "\\\""); return raw; } } // namespace ledger ������������������������������������������������������������������������������������ledger-3.3.2/src/emacs.h����������������������������������������������������������������������������0000664�0000000�0000000�00000004544�14411236400�0015013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup report */ /** * @file emacs.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_EMACS_H #define INCLUDED_EMACS_H #include "chain.h" namespace ledger { class xact_t; class format_emacs_posts : public item_handler<post_t> { format_emacs_posts(); protected: std::ostream& out; xact_t * last_xact; public: format_emacs_posts(std::ostream& _out) : out(_out), last_xact(NULL) { TRACE_CTOR(format_emacs_posts, "std::ostream&"); } ~format_emacs_posts() { TRACE_DTOR(format_emacs_posts); } virtual void write_xact(xact_t& xact); virtual void flush() { if (last_xact) out << "))\n"; out.flush(); } virtual void operator()(post_t& post); virtual string escape_string(string raw); }; } // namespace ledger #endif // INCLUDED_EMACS_H ������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/error.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000007157�14411236400�0015215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "utils.h" #if HAVE_GPGME #include "gpgme.h" #endif namespace ledger { std::ostringstream _ctxt_buffer; std::ostringstream _desc_buffer; string error_context() { string context = _ctxt_buffer.str(); _ctxt_buffer.clear(); _ctxt_buffer.str(""); return context; } string file_context(const path& file, const std::size_t line) { std::ostringstream buf; buf << '"' << file.string() << "\", line " << line << ":"; return buf.str(); } string line_context(const string& line, const string::size_type pos, const string::size_type end_pos) { std::ostringstream buf; buf << " " << line << "\n"; if (pos != 0) { buf << " "; if (end_pos == 0) { for (string::size_type i = 0; i < pos; i += 1) buf << " "; buf << "^"; } else { for (string::size_type i = 0; i < end_pos; i += 1) { if (i >= pos) buf << "^"; else buf << " "; } } } return buf.str(); } string source_context(const path& file, const std::istream::pos_type pos, const std::istream::pos_type end_pos, const string& prefix) { const std::streamoff len = end_pos - pos; if (! len || file.empty()) return _("<no source context>"); assert(len > 0); assert(len < 65536); std::ostringstream out; #if HAVE_GPGME std::istream* in(decrypted_stream_t::open_stream(file)); #else std::istream* in(new ifstream(file)); #endif in->seekg(pos, std::ios::beg); scoped_array<char> buf(new char[static_cast<std::size_t>(len) + 1]); in->read(buf.get(), static_cast<std::streamsize>(len)); assert(in->gcount() == static_cast<std::streamsize>(len)); buf[static_cast<std::ptrdiff_t>(len)] = '\0'; bool first = true; for (char * p = std::strtok(buf.get(), "\n"); p; p = std::strtok(NULL, "\n")) { if (first) first = false; else out << '\n'; out << prefix << p; } delete(in); return out.str(); } } // namespace ledger �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/error.h����������������������������������������������������������������������������0000664�0000000�0000000�00000007170�14411236400�0015052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup util */ /** * @file error.h * @author John Wiegley * * @ingroup util */ #ifndef INCLUDED_ERROR_H #define INCLUDED_ERROR_H namespace ledger { extern std::ostringstream _desc_buffer; template <typename T> [[ noreturn ]] inline void throw_func(const string& message) { _desc_buffer.clear(); _desc_buffer.str(""); throw T(message); } #define throw_(cls, msg) \ ((_desc_buffer << (msg)), \ throw_func<cls>(_desc_buffer.str())) inline void warning_func(const string& message) { std::cerr << "Warning: " << message << std::endl; _desc_buffer.clear(); _desc_buffer.str(""); } #define warning_(msg) \ ((_desc_buffer << (msg)), \ warning_func(_desc_buffer.str())) extern std::ostringstream _ctxt_buffer; #define add_error_context(msg) \ ((long(_ctxt_buffer.tellp()) == 0) ? \ (_ctxt_buffer << (msg)) : \ (_ctxt_buffer << std::endl << (msg))) string error_context(); string file_context(const path& file, std::size_t line); string line_context(const string& line, const string::size_type pos = 0, const string::size_type end_pos = 0); string source_context(const path& file, const std::istream::pos_type pos, const std::istream::pos_type end_pos, const string& prefix = ""); #define DECLARE_EXCEPTION(name, kind) \ class name : public kind { \ public: \ explicit name(const string& why) throw() : kind(why) {} \ virtual ~name() throw() {} \ } struct error_count { std::size_t count; std::string message; explicit error_count(std::size_t _count, std::string _msg) : count(_count), message(_msg) {} const char * what() const { return message.c_str(); } }; } // namespace ledger #endif // INCLUDED_ERROR_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/expr.cc����������������������������������������������������������������������������0000664�0000000�0000000�00000016762�14411236400�0015044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "expr.h" #include "parser.h" #include "scope.h" #include <boost/smart_ptr/scoped_ptr.hpp> namespace ledger { expr_t::expr_t() : base_type() { TRACE_CTOR(expr_t, ""); } expr_t::expr_t(const expr_t& other) : base_type(other), ptr(other.ptr) { TRACE_CTOR(expr_t, "copy"); } expr_t::expr_t(ptr_op_t _ptr, scope_t * _context) : base_type(_context), ptr(_ptr) { TRACE_CTOR(expr_t, "const ptr_op_t&, scope_t *"); } expr_t::expr_t(const string& _str, const parse_flags_t& flags) : base_type() { if (! _str.empty()) parse(_str, flags); TRACE_CTOR(expr_t, "string, parse_flags_t"); } expr_t::expr_t(std::istream& in, const parse_flags_t& flags) : base_type() { parse(in, flags); TRACE_CTOR(expr_t, "std::istream&, parse_flags_t"); } expr_t::~expr_t() { TRACE_DTOR(expr_t); } expr_t& expr_t::operator=(const expr_t& _expr) { if (this != &_expr) { base_type::operator=(_expr); ptr = _expr.ptr; } return *this; } expr_t::operator bool() const throw() { return ptr.get() != NULL; } expr_t::ptr_op_t expr_t::get_op() throw() { return ptr; } void expr_t::parse(std::istream& in, const parse_flags_t& flags, const optional<string>& original_string) { parser_t parser; std::istream::pos_type start_pos = in.tellg(); ptr = parser.parse(in, flags, original_string); std::istream::pos_type end_pos = in.tellg(); if (original_string) { set_text(*original_string); } else if (end_pos > start_pos) { in.clear(); in.seekg(start_pos, std::ios::beg); scoped_array<char> buf (new char[static_cast<std::size_t>(end_pos - start_pos) + 1]); int len = static_cast<int>(end_pos) - static_cast<int>(start_pos); in.read(buf.get(), len); buf[len] = '\0'; set_text(buf.get()); } else { set_text("<stream>"); } } void expr_t::compile(scope_t& scope) { if (! compiled && ptr) { ptr = ptr->compile(scope); base_type::compile(scope); } } value_t expr_t::real_calc(scope_t& scope) { if (ptr) { ptr_op_t locus; try { return ptr->calc(scope, &locus); } catch (const std::exception&) { if (locus) { string current_context = error_context(); add_error_context(_("While evaluating value expression:")); add_error_context(op_context(ptr, locus)); if (SHOW_INFO()) { add_error_context(_("The value expression tree was:")); std::ostringstream buf; ptr->dump(buf, 0); std::istringstream in(buf.str()); std::ostringstream out; char linebuf[1024]; bool first = true; while (in.good() && ! in.eof()) { in.getline(linebuf, 1023); std::streamsize len = in.gcount(); if (len > 0) { if (first) first = false; else out << '\n'; out << " " << linebuf; } } add_error_context(out.str()); } if (! current_context.empty()) add_error_context(current_context); } throw; } } return NULL_VALUE; } bool expr_t::is_constant() const { assert(compiled); return ptr && ptr->is_value(); } bool expr_t::is_function() const { assert(compiled); return ptr && ptr->is_function(); } value_t& expr_t::constant_value() { assert(is_constant()); return ptr->as_value_lval(); } const value_t& expr_t::constant_value() const { assert(is_constant()); return ptr->as_value(); } expr_t::func_t& expr_t::get_function() { assert(is_function()); return ptr->as_function_lval(); } string expr_t::context_to_str() const { return ptr ? op_context(ptr) : _("<empty expression>"); } void expr_t::print(std::ostream& out) const { if (ptr) ptr->print(out); } void expr_t::dump(std::ostream& out) const { if (ptr) ptr->dump(out, 0); } bool merged_expr_t::check_for_single_identifier(const string& expr) { bool single_identifier = true; for (const char * p = expr.c_str(); *p; ++p) if (! std::isalnum(*p) || *p == '_') { single_identifier = false; break; } if (single_identifier) { set_base_expr(expr); exprs.clear(); return true; } else { return false; } } void merged_expr_t::compile(scope_t& scope) { if (exprs.empty()) { parse(base_expr); } else { std::ostringstream buf; buf << "__tmp_" << term << "=(" << term << "=(" << base_expr << ")"; foreach (const string& expr, exprs) { if (merge_operator == ";") buf << merge_operator << term << "=" << expr; else buf << merge_operator << "(" << expr << ")"; } buf << ";" << term << ");__tmp_" << term; DEBUG("expr.merged.compile", "Compiled expr: " << buf.str()); parse(buf.str()); } expr_t::compile(scope); } expr_t::ptr_op_t as_expr(const value_t& val) { VERIFY(val.is_any()); return val.as_any<expr_t::ptr_op_t>(); } void set_expr(value_t& val, expr_t::ptr_op_t op) { val.set_any(op); } value_t expr_value(expr_t::ptr_op_t op) { value_t temp; temp.set_any(op); return temp; } value_t source_command(call_scope_t& args) { std::istream * in = NULL; scoped_ptr<ifstream> stream; string pathname; if (args.has(0)) { pathname = args.get<string>(0); stream.reset(new ifstream(path(pathname))); in = stream.get(); } else { pathname = "<stdin>"; in = &std::cin; } symbol_scope_t file_locals(args); std::size_t linenum = 0; char buf[4096]; std::istream::pos_type pos; while (in->good() && ! in->eof()) { pos = in->tellg(); in->getline(buf, 4095); linenum++; char * p = skip_ws(buf); if (*p && *p != ';') { try { expr_t(p).calc(file_locals); } catch (const std::exception&) { add_error_context(_f("While parsing value expression on line %1%:") % linenum); add_error_context(source_context(pathname, pos, in->tellg(), "> ")); } } } return true; } } // namespace ledger ��������������ledger-3.3.2/src/expr.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000012532�14411236400�0014675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file expr.h * @author John Wiegley * * @ingroup expr */ #ifndef INCLUDED_EXPR_H #define INCLUDED_EXPR_H #include "exprbase.h" #include "value.h" namespace ledger { class expr_t : public expr_base_t<value_t> { class parser_t; typedef expr_base_t<value_t> base_type; public: struct token_t; class op_t; typedef intrusive_ptr<op_t> ptr_op_t; typedef intrusive_ptr<const op_t> const_ptr_op_t; enum check_expr_kind_t { EXPR_GENERAL, EXPR_ASSERTION, EXPR_CHECK }; typedef std::pair<expr_t, check_expr_kind_t> check_expr_pair; typedef std::list<check_expr_pair> check_expr_list; protected: ptr_op_t ptr; public: expr_t(); expr_t(const expr_t& other); expr_t(ptr_op_t _ptr, scope_t * _context = NULL); expr_t(const string& _str, const parse_flags_t& flags = PARSE_DEFAULT); expr_t(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT); virtual ~expr_t(); expr_t& operator=(const expr_t& _expr); virtual operator bool() const throw(); ptr_op_t get_op() throw(); void parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) { std::istringstream stream(str); return parse(stream, flags, str); } virtual void parse(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT, const optional<string>& original_string = none); virtual void compile(scope_t& scope); virtual value_t real_calc(scope_t& scope); bool is_constant() const; value_t& constant_value(); const value_t& constant_value() const; bool is_function() const; func_t& get_function(); virtual string context_to_str() const; virtual void print(std::ostream& out) const; virtual void dump(std::ostream& out) const; }; /** * Dealing with expr pointers tucked into value objects. */ inline bool is_expr(const value_t& val) { return val.is_any() && val.as_any().type() == typeid(expr_t::ptr_op_t); } expr_t::ptr_op_t as_expr(const value_t& val); void set_expr(value_t& val, expr_t::ptr_op_t op); value_t expr_value(expr_t::ptr_op_t op); // A merged expression allows one to set an expression term, "foo", and // a base expression, "bar", and then merge in later expressions that // utilize foo. For example: // // foo: bar // merge: foo * 10 // merge: foo + 20 // // When this expression is finally compiled, the base and merged // elements are written into this: // // __tmp=(foo=bar; foo=foo*10; foo=foo+20);__tmp // // This allows users to select flags like -O, -B or -I at any time, and // also combine flags such as -V and -A. class merged_expr_t : public expr_t { public: string term; string base_expr; string merge_operator; std::list<string> exprs; merged_expr_t(const string& _term, const string& expr, const string& merge_op = ";") : expr_t(), term(_term), base_expr(expr), merge_operator(merge_op) { TRACE_CTOR(merged_expr_t, "string, string, string"); } virtual ~merged_expr_t() { TRACE_DTOR(merged_expr_t); } void set_term(const string& _term) { term = _term; } void set_base_expr(const string& expr) { base_expr = expr; } void set_merge_operator(const string& merge_op) { merge_operator = merge_op; } bool check_for_single_identifier(const string& expr); void prepend(const string& expr) { if (! check_for_single_identifier(expr)) exprs.push_front(expr); } void append(const string& expr) { if (! check_for_single_identifier(expr)) exprs.push_back(expr); } void remove(const string& expr) { exprs.remove(expr); } virtual void compile(scope_t& scope); }; class call_scope_t; value_t source_command(call_scope_t& scope); } // namespace ledger #endif // INCLUDED_EXPR_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/exprbase.h�������������������������������������������������������������������������0000664�0000000�0000000�00000015071�14411236400�0015531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file exprbase.h * @author John Wiegley * * @ingroup expr * * This class provides basic behavior for all the domain specific expression * languages used in Leger: * * | Typename | Description | result_type | Derives | * |-------------+----------------------------+-----------------+-------------| * | expr_t | Value expressions | value_t | | * | predicate_t | Special form of expr_t | bool | expr_t | * | query_t | Report queries | bool | predicate_t | * | period_t | Time periods and durations | date_interval_t | | * | draft_t | Partially filled xacts | xact_t * | | * | format_t | Format strings | string | | */ #ifndef INCLUDED_EXPRBASE_H #define INCLUDED_EXPRBASE_H #include "utils.h" #include "amount.h" namespace ledger { DECLARE_EXCEPTION(parse_error, std::runtime_error); DECLARE_EXCEPTION(compile_error, std::runtime_error); DECLARE_EXCEPTION(calc_error, std::runtime_error); DECLARE_EXCEPTION(usage_error, std::runtime_error); class scope_t; class call_scope_t; template <typename ResultType> class expr_base_t { public: typedef ResultType result_type; typedef function<result_type (call_scope_t&)> func_t; protected: scope_t * context; string str; bool compiled; virtual result_type real_calc(scope_t& scope) = 0; public: expr_base_t(const expr_base_t& other) : context(other.context), str(other.str), compiled(false) { TRACE_CTOR(expr_base_t, "copy"); } expr_base_t(scope_t * _context = NULL) : context(_context), compiled(false) { TRACE_CTOR(expr_base_t, "scope_t *"); } virtual ~expr_base_t() { TRACE_DTOR(expr_base_t); } expr_base_t& operator=(const expr_base_t& _expr) { if (this != &_expr) { str = _expr.str; context = _expr.context; compiled = _expr.compiled; } return *this; } expr_base_t& operator=(const string& _expr) { parse(_expr); return *this; } virtual operator bool() const throw() { return ! str.empty(); } virtual string text() const throw() { return str; } void set_text(const string& txt) { str = txt; compiled = false; } void parse(const string& expr_str, const parse_flags_t& flags = PARSE_DEFAULT) { std::istringstream stream(expr_str); return parse(stream, flags, expr_str); } virtual void parse(std::istream&, const parse_flags_t& = PARSE_DEFAULT, const optional<string>& original_string = none) { set_text(original_string ? *original_string : "<stream>"); } virtual void mark_uncompiled() { compiled = false; } void recompile(scope_t& scope) { compiled = false; compile(scope); } virtual void compile(scope_t& scope) { if (! compiled) { // Derived classes need to do something here. context = &scope; compiled = true; } } result_type operator()(scope_t& scope) { return calc(scope); } result_type calc(scope_t& scope) { if (! compiled) { #if DEBUG_ON if (SHOW_DEBUG("expr.compile")) { DEBUG("expr.compile", "Before compilation:"); dump(*_log_stream); } #endif // DEBUG_ON DEBUG("expr.compile", "Compiling: " << str); compile(scope); #if DEBUG_ON if (SHOW_DEBUG("expr.compile")) { DEBUG("expr.compile", "After compilation:"); dump(*_log_stream); } #endif // DEBUG_ON } DEBUG("expr.calc", "Calculating: " << str); return real_calc(scope); } result_type calc() { assert(context); return calc(*context); } scope_t * get_context() { return context; } void set_context(scope_t * scope) { context = scope; } virtual string context_to_str() const { return empty_string; } string print_to_str() const { std::ostringstream out; print(out); return out.str(); } string dump_to_str() const { std::ostringstream out; dump(out); return out.str(); } string preview_to_str(scope_t&) const { std::ostringstream out; preview(out); return out.str(); } virtual void print(std::ostream&) const {} virtual void dump(std::ostream&) const {} result_type preview(std::ostream& out, scope_t& scope) const { out << _("--- Input expression ---") << std::endl; out << text() << std::endl; out << std::endl << _("--- Text as parsed ---") << std::endl; print(out); out << std::endl; out << std::endl << _("--- Expression tree ---") << std::endl; dump(out); out << std::endl << _("--- Compiled tree ---") << std::endl; compile(scope); dump(out); out << std::endl << _("--- Result value ---") << std::endl; return calc(); } }; template <typename ResultType> std::ostream& operator<<(std::ostream& out, const expr_base_t<ResultType>& expr) { expr.print(out); return out; } } // namespace ledger #endif // INCLUDED_EXPRBASE_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/filters.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000133607�14411236400�0015534�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "filters.h" #include "iterators.h" #include "journal.h" #include "report.h" #include "compare.h" #include "pool.h" namespace ledger { void post_splitter::print_title(const value_t& val) { if (! report.HANDLED(no_titles)) { std::ostringstream buf; val.print(buf); post_chain->title(buf.str()); } } void post_splitter::flush() { foreach (value_to_posts_map::value_type& pair, posts_map) { preflush_func(pair.first); foreach (post_t * post, pair.second) (*post_chain)(*post); post_chain->flush(); post_chain->clear(); if (postflush_func) (*postflush_func)(pair.first); } } void post_splitter::operator()(post_t& post) { bind_scope_t bound_scope(report, post); value_t result(group_by_expr.calc(bound_scope)); if (! result.is_null()) { value_to_posts_map::iterator i = posts_map.find(result); if (i != posts_map.end()) { (*i).second.push_back(&post); } else { std::pair<value_to_posts_map::iterator, bool> inserted = posts_map.insert(value_to_posts_map::value_type(result, posts_list())); assert(inserted.second); (*inserted.first).second.push_back(&post); } } } void truncate_xacts::flush() { if (! posts.size()) return; xact_t * xact = (*posts.begin())->xact; int l = 0; foreach (post_t * post, posts) if (xact != post->xact) { l++; xact = post->xact; } l++; xact = (*posts.begin())->xact; int i = 0; foreach (post_t * post, posts) { if (xact != post->xact) { xact = post->xact; i++; } bool print = false; if (head_count) { if (head_count > 0 && i < head_count) print = true; else if (head_count < 0 && i >= - head_count) print = true; } if (! print && tail_count) { if (tail_count > 0 && l - i <= tail_count) print = true; else if (tail_count < 0 && l - i > - tail_count) print = true; } if (print) item_handler<post_t>::operator()(*post); } posts.clear(); item_handler<post_t>::flush(); } void truncate_xacts::operator()(post_t& post) { if (completed) return; if (last_xact != post.xact) { if (last_xact) xacts_seen++; last_xact = post.xact; } if (tail_count == 0 && head_count > 0 && static_cast<int>(xacts_seen) >= head_count) { flush(); completed = true; return; } posts.push_back(&post); } void sort_posts::post_accumulated_posts() { std::stable_sort(posts.begin(), posts.end(), compare_items<post_t>(sort_order, report)); foreach (post_t * post, posts) { post->xdata().drop_flags(POST_EXT_SORT_CALC); item_handler<post_t>::operator()(*post); } posts.clear(); } namespace { void split_string(const string& str, const char ch, std::list<string>& strings) { const char * b = str.c_str(); for (const char * p = b; *p; p++) { if (*p == ch) { strings.push_back(string(b, static_cast<std::string::size_type>(p - b))); b = p + 1; } } strings.push_back(string(b)); } account_t * create_temp_account_from_path(std::list<string>& account_names, temporaries_t& temps, account_t * master) { account_t * new_account = NULL; foreach (const string& name, account_names) { if (new_account) { new_account = new_account->find_account(name); } else { new_account = master->find_account(name, false); if (! new_account) new_account = &temps.create_account(name, master); } } assert(new_account != NULL); return new_account; } } void anonymize_posts::render_commodity(amount_t& amt) { commodity_t& comm(amt.commodity()); std::size_t id; bool newly_added = false; commodity_index_map::iterator i = comms.find(&comm); if (i == comms.end()) { id = next_comm_id++; newly_added = true; comms.insert(commodity_index_map::value_type(&comm, id)); } else { id = (*i).second; } std::ostringstream buf; do { buf << static_cast<char>('A' + (id % 26)); id /= 26; } while (id > 0); if (amt.has_annotation()) amt.set_commodity (*commodity_pool_t::current_pool->find_or_create(buf.str(), amt.annotation())); else amt.set_commodity (*commodity_pool_t::current_pool->find_or_create(buf.str())); if (newly_added) { amt.commodity().set_flags(comm.flags()); amt.commodity().set_precision(comm.precision()); } } void anonymize_posts::operator()(post_t& post) { boost::uuids::detail::sha1 sha; unsigned int message_digest[5]; bool copy_xact_details = false; if (last_xact != post.xact) { temps.copy_xact(*post.xact); last_xact = post.xact; copy_xact_details = true; } xact_t& xact = temps.last_xact(); xact.code = none; if (copy_xact_details) { xact.copy_details(*post.xact); std::ostringstream buf; buf << reinterpret_cast<boost::uintmax_t>(post.xact->payee.c_str()) << integer_gen() << post.xact->payee.c_str(); sha.reset(); sha.process_bytes(buf.str().c_str(), buf.str().length()); sha.get_digest(message_digest); xact.payee = to_hex(message_digest); xact.note = none; } else { xact.journal = post.xact->journal; } std::list<string> account_names; for (account_t * acct = post.account; acct; acct = acct->parent) { std::ostringstream buf; buf << integer_gen() << acct << acct->fullname(); sha.reset(); sha.process_bytes(buf.str().c_str(), buf.str().length()); sha.get_digest(message_digest); account_names.push_front(to_hex(message_digest)); } account_t * new_account = create_temp_account_from_path(account_names, temps, xact.journal->master); post_t& temp = temps.copy_post(post, xact, new_account); temp.note = none; temp.add_flags(POST_ANONYMIZED); render_commodity(temp.amount); if (temp.amount.has_annotation()) { temp.amount.annotation().tag = none; if (temp.amount.annotation().price) render_commodity(*temp.amount.annotation().price); } if (temp.cost) render_commodity(*temp.cost); if (temp.assigned_amount) render_commodity(*temp.assigned_amount); (*handler)(temp); } void calc_posts::operator()(post_t& post) { post_t::xdata_t& xdata(post.xdata()); if (last_post) { assert(last_post->has_xdata()); if (calc_running_total) xdata.total = last_post->xdata().total; xdata.count = last_post->xdata().count + 1; } else { xdata.count = 1; } post.add_to_value(xdata.visited_value, amount_expr); xdata.add_flags(POST_EXT_VISITED); account_t * acct = post.reported_account(); acct->xdata().add_flags(ACCOUNT_EXT_VISITED); if (calc_running_total) add_or_set_value(xdata.total, xdata.visited_value); item_handler<post_t>::operator()(post); last_post = &post; } namespace { void handle_value(const value_t& value, account_t * account, xact_t * xact, temporaries_t& temps, post_handler_ptr handler, const date_t& date = date_t(), const bool act_date_p = true, const value_t& total = value_t(), const bool direct_amount = false, const bool mark_visited = false, const bool bidir_link = true) { post_t& post = temps.create_post(*xact, account, bidir_link); post.add_flags(ITEM_GENERATED); // If the account for this post is all virtual, then report the post as // such. This allows subtotal reports to show "(Account)" for accounts // that contain only virtual posts. if (account && account->has_xdata() && account->xdata().has_flags(ACCOUNT_EXT_AUTO_VIRTUALIZE)) { if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS)) { post.add_flags(POST_VIRTUAL); if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_UNB_VIRTUALS)) post.add_flags(POST_MUST_BALANCE); } } post_t::xdata_t& xdata(post.xdata()); if (is_valid(date)) { if (act_date_p) xdata.date = date; else xdata.value_date = date; } value_t temp(value); switch (value.type()) { case value_t::BOOLEAN: case value_t::INTEGER: temp.in_place_cast(value_t::AMOUNT); // fall through... case value_t::AMOUNT: post.amount = temp.as_amount(); break; case value_t::BALANCE: case value_t::SEQUENCE: xdata.compound_value = temp; xdata.add_flags(POST_EXT_COMPOUND); break; case value_t::DATETIME: case value_t::DATE: default: assert(false); break; } if (! total.is_null()) xdata.total = total; if (direct_amount) xdata.add_flags(POST_EXT_DIRECT_AMT); DEBUG("filters.changed_value.rounding", "post.amount = " << post.amount); (*handler)(post); if (mark_visited) { post.xdata().add_flags(POST_EXT_VISITED); post.account->xdata().add_flags(ACCOUNT_EXT_VISITED); } } } void collapse_posts::report_subtotal() { if (! count) return; std::size_t displayed_count = 0; foreach (post_t * post, component_posts) { bind_scope_t bound_scope(report, *post); if (only_predicate(bound_scope) && display_predicate(bound_scope)) displayed_count++; } if (collapse_depth == 0 && displayed_count == 1) { item_handler<post_t>::operator()(*last_post); } else if (only_collapse_if_zero && ! subtotal.is_zero()) { foreach (post_t * post, component_posts) item_handler<post_t>::operator()(*post); } else { date_t earliest_date; date_t latest_date; foreach (post_t * post, component_posts) { date_t date = post->date(); date_t value_date = post->value_date(); if (! is_valid(earliest_date) || date < earliest_date) earliest_date = date; if (! is_valid(latest_date) || value_date > latest_date) latest_date = value_date; } xact_t& xact = temps.create_xact(); xact.payee = last_xact->payee; xact._date = (is_valid(earliest_date) ? earliest_date : last_xact->_date); DEBUG("filters.collapse", "Pseudo-xact date = " << *xact._date); DEBUG("filters.collapse", "earliest date = " << earliest_date); DEBUG("filters.collapse", "latest date = " << latest_date); foreach (totals_map::value_type& pat, totals) { handle_value(/* value= */ pat.second, /* account= */ pat.first, /* xact= */ &xact, /* temps= */ temps, /* handler= */ handler, /* date= */ latest_date, /* act_date_p= */ false); } } totals.clear(); component_posts.clear(); last_xact = NULL; last_post = NULL; subtotal = 0L; count = 0; } value_t& collapse_posts::find_totals(account_t* account) { if (collapse_depth == 0) return totals[global_totals_account]; if (account->depth <= collapse_depth) return totals[account]; //else recurse return find_totals(account->parent); } void collapse_posts::operator()(post_t& post) { // If we've reached a new xact, report on the subtotal // accumulated thus far. if (last_xact != post.xact && count > 0) report_subtotal(); post.add_to_value(subtotal, amount_expr); post.add_to_value(find_totals(post.account), amount_expr); component_posts.push_back(&post); last_xact = post.xact; last_post = &post; count++; } void related_posts::flush() { if (posts.size() > 0) { foreach (post_t * post, posts) { assert(post->xact); foreach (post_t * r_post, post->xact->posts) { post_t::xdata_t& xdata(r_post->xdata()); if (! xdata.has_flags(POST_EXT_HANDLED) && (! xdata.has_flags(POST_EXT_RECEIVED) ? ! r_post->has_flags(ITEM_GENERATED | POST_VIRTUAL) : also_matching)) { xdata.add_flags(POST_EXT_HANDLED); item_handler<post_t>::operator()(*r_post); } } } } item_handler<post_t>::flush(); } display_filter_posts::display_filter_posts(post_handler_ptr handler, report_t& _report, bool _show_rounding) : item_handler<post_t>(handler), report(_report), display_amount_expr(report.HANDLER(display_amount_).expr), display_total_expr(report.HANDLER(display_total_).expr), show_rounding(_show_rounding) { create_accounts(); TRACE_CTOR(display_filter_posts, "post_handler_ptr, report_t&, bool"); } bool display_filter_posts::output_rounding(post_t& post) { bind_scope_t bound_scope(report, post); value_t new_display_total; if (show_rounding) { new_display_total = (display_total_expr.calc(bound_scope) .strip_annotations(report.what_to_keep())); DEBUG("filters.changed_value.rounding", "rounding.new_display_total = " << new_display_total); } // Allow the posting to be displayed if: // 1. Its display_amount would display as non-zero, or // 2. The --empty option was specified, or // 3. a) The account of the posting is <Revalued>, and // b) the revalued option is specified, and // c) the --no-rounding option is not specified. if (post.account == revalued_account) { if (show_rounding) last_display_total = new_display_total; return true; } if (value_t repriced_amount = (display_amount_expr.calc(bound_scope) .strip_annotations(report.what_to_keep()))) { if (! last_display_total.is_null()) { DEBUG("filters.changed_value.rounding", "rounding.repriced_amount = " << repriced_amount); value_t precise_display_total(new_display_total.truncated() - repriced_amount.truncated()); DEBUG("filters.changed_value.rounding", "rounding.precise_display_total = " << precise_display_total); DEBUG("filters.changed_value.rounding", "rounding.last_display_total = " << last_display_total); if (value_t diff = precise_display_total - last_display_total) { DEBUG("filters.changed_value.rounding", "rounding.diff = " << diff); handle_value(/* value= */ diff, /* account= */ rounding_account, /* xact= */ post.xact, /* temps= */ temps, /* handler= */ handler, /* date= */ date_t(), /* act_date_p= */ true, /* total= */ precise_display_total, /* direct_amount= */ true, /* mark_visited= */ false, /* bidir_link= */ false); } } if (show_rounding) last_display_total = new_display_total; return true; } else { return report.HANDLED(empty); } } void display_filter_posts::operator()(post_t& post) { if (output_rounding(post)) item_handler<post_t>::operator()(post); } changed_value_posts::changed_value_posts (post_handler_ptr handler, report_t& _report, bool _for_accounts_report, bool _show_unrealized, display_filter_posts * _display_filter) : item_handler<post_t>(handler), report(_report), total_expr(report.HANDLED(revalued_total_) ? report.HANDLER(revalued_total_).expr : report.HANDLER(display_total_).expr), display_total_expr(report.HANDLER(display_total_).expr), changed_values_only(report.HANDLED(revalued_only)), historical_prices_only(report.HANDLED(historical)), for_accounts_report(_for_accounts_report), show_unrealized(_show_unrealized), last_post(NULL), display_filter(_display_filter) { string gains_equity_account_name; if (report.HANDLED(unrealized_gains_)) gains_equity_account_name = report.HANDLER(unrealized_gains_).str(); else gains_equity_account_name = _("Equity:Unrealized Gains"); gains_equity_account = report.session.journal->master->find_account(gains_equity_account_name); gains_equity_account->add_flags(ACCOUNT_GENERATED); string losses_equity_account_name; if (report.HANDLED(unrealized_losses_)) losses_equity_account_name = report.HANDLER(unrealized_losses_).str(); else losses_equity_account_name = _("Equity:Unrealized Losses"); losses_equity_account = report.session.journal->master->find_account(losses_equity_account_name); losses_equity_account->add_flags(ACCOUNT_GENERATED); create_accounts(); TRACE_CTOR(changed_value_posts, "post_handler_ptr, report_t&, bool, bool, display_filter_posts *"); } void changed_value_posts::flush() { if (last_post && last_post->date() <= report.terminus.date()) { if (! historical_prices_only) { if (! for_accounts_report) output_intermediate_prices(*last_post, report.terminus.date()); output_revaluation(*last_post, report.terminus.date()); } last_post = NULL; } item_handler<post_t>::flush(); } void changed_value_posts::output_revaluation(post_t& post, const date_t& date) { if (is_valid(date)) post.xdata().date = date; try { bind_scope_t bound_scope(report, post); repriced_total = total_expr.calc(bound_scope); } catch (...) { post.xdata().date = date_t(); throw; } post.xdata().date = date_t(); DEBUG("filters.changed_value", "output_revaluation(last_total) = " << last_total); DEBUG("filters.changed_value", "output_revaluation(repriced_total) = " << repriced_total); if (! last_total.is_null()) { if (value_t diff = repriced_total - last_total) { DEBUG("filters.changed_value", "output_revaluation(strip(diff)) = " << diff.strip_annotations(report.what_to_keep())); xact_t& xact = temps.create_xact(); xact.payee = _("Commodities revalued"); xact._date = is_valid(date) ? date : post.value_date(); if (! for_accounts_report) { handle_value (/* value= */ diff, /* account= */ revalued_account, /* xact= */ &xact, /* temps= */ temps, /* handler= */ handler, /* date= */ *xact._date, /* act_date_p= */ true, /* total= */ repriced_total); } else if (show_unrealized) { handle_value (/* value= */ - diff, /* account= */ (diff < 0L ? losses_equity_account : gains_equity_account), /* xact= */ &xact, /* temps= */ temps, /* handler= */ handler, /* date= */ *xact._date, /* act_date_p= */ true, /* total= */ value_t(), /* direct_amount= */ false, /* mark_visited= */ true); } } } } namespace { struct insert_prices_in_map { price_map_t& all_prices; insert_prices_in_map(price_map_t& _all_prices) : all_prices(_all_prices) {} void operator()(const datetime_t& date, const amount_t& price) { all_prices.insert(price_map_t::value_type(date, price)); } }; } void changed_value_posts::output_intermediate_prices(post_t& post, const date_t& current) { // To fix BZ#199, examine the balance of last_post and determine whether the // price of that amount changed after its date and before the new post's // date. If so, generate an output_revaluation for that price change. // Mostly this is only going to occur if the user has a series of pricing // entries, since a posting-based revaluation would be seen here as a post. value_t display_total(last_total); if (display_total.type() == value_t::SEQUENCE) { xact_t& xact(temps.create_xact()); xact.payee = _("Commodities revalued"); xact._date = is_valid(current) ? current : post.value_date(); post_t& temp(temps.copy_post(post, xact)); temp.add_flags(ITEM_GENERATED); post_t::xdata_t& xdata(temp.xdata()); if (is_valid(current)) xdata.date = current; DEBUG("filters.revalued", "intermediate last_total = " << last_total); switch (last_total.type()) { case value_t::BOOLEAN: case value_t::INTEGER: last_total.in_place_cast(value_t::AMOUNT); // fall through... case value_t::AMOUNT: temp.amount = last_total.as_amount(); break; case value_t::BALANCE: case value_t::SEQUENCE: xdata.compound_value = last_total; xdata.add_flags(POST_EXT_COMPOUND); break; case value_t::DATETIME: case value_t::DATE: default: assert(false); break; } bind_scope_t inner_scope(report, temp); display_total = display_total_expr.calc(inner_scope); DEBUG("filters.revalued", "intermediate display_total = " << display_total); } switch (display_total.type()) { case value_t::VOID: case value_t::INTEGER: case value_t::SEQUENCE: break; case value_t::AMOUNT: display_total.in_place_cast(value_t::BALANCE); // fall through... case value_t::BALANCE: { price_map_t all_prices; foreach (const balance_t::amounts_map::value_type& amt_comm, display_total.as_balance().amounts) amt_comm.first->map_prices(insert_prices_in_map(all_prices), datetime_t(current), datetime_t(post.value_date()), true); // Choose the last price from each day as the price to use typedef std::map<const date_t, bool> date_map; date_map pricing_dates; BOOST_REVERSE_FOREACH(const price_map_t::value_type& price, all_prices) { // This insert will fail if a later price has already been inserted // for that date. DEBUG("filters.revalued", "re-inserting " << price.second << " at " << price.first.date()); pricing_dates.insert(date_map::value_type(price.first.date(), true)); } // Go through the time-sorted prices list, outputting a revaluation for // each price difference. foreach (const date_map::value_type& price, pricing_dates) { output_revaluation(post, price.first); last_total = repriced_total; } break; } default: assert(false); break; } } void changed_value_posts::operator()(post_t& post) { if (last_post) { if (! for_accounts_report && ! historical_prices_only) output_intermediate_prices(*last_post, post.value_date()); output_revaluation(*last_post, post.value_date()); } if (changed_values_only) post.xdata().add_flags(POST_EXT_DISPLAYED); item_handler<post_t>::operator()(post); bind_scope_t bound_scope(report, post); last_total = total_expr.calc(bound_scope); last_post = &post; } void subtotal_posts::report_subtotal(const char * spec_fmt, const optional<date_interval_t>& interval) { if (component_posts.empty()) return; optional<date_t> range_start = interval ? interval->start : none; optional<date_t> range_finish = interval ? interval->inclusive_end() : none; if (! range_start || ! range_finish) { foreach (post_t * post, component_posts) { date_t date = post->date(); date_t value_date = post->value_date(); #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif if (! range_start || date < *range_start) range_start = date; if (! range_finish || value_date > *range_finish) range_finish = value_date; #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 #pragma GCC diagnostic pop #endif } } component_posts.clear(); std::ostringstream out_date; if (spec_fmt) { out_date << format_date(*range_finish, FMT_CUSTOM, spec_fmt); } else if (date_format) { out_date << "- " << format_date(*range_finish, FMT_CUSTOM, date_format->c_str()); } else { out_date << "- " << format_date(*range_finish); } xact_t& xact = temps.create_xact(); xact.payee = out_date.str(); xact._date = *range_start; foreach (values_map::value_type& pair, values) handle_value(/* value= */ pair.second.value, /* account= */ pair.second.account, /* xact= */ &xact, /* temps= */ temps, /* handler= */ handler, /* date= */ *range_finish, /* act_date_p= */ false); values.clear(); } void subtotal_posts::operator()(post_t& post) { component_posts.push_back(&post); account_t * acct = post.reported_account(); assert(acct); value_t amount(post.amount); post.xdata().compound_value = amount; post.xdata().add_flags(POST_EXT_COMPOUND); values_map::iterator i = values.find(acct->fullname()); if (i == values.end()) { #if DEBUG_ON std::pair<values_map::iterator, bool> result = #endif values.insert(values_pair (acct->fullname(), acct_value_t(acct, amount, post.has_flags(POST_VIRTUAL), post.has_flags(POST_MUST_BALANCE)))); #if DEBUG_ON assert(result.second); #endif } else { if (post.has_flags(POST_VIRTUAL) != (*i).second.is_virtual) throw_(std::logic_error, _("'equity' cannot accept virtual and " "non-virtual postings to the same account")); add_or_set_value((*i).second.value, amount); } // If the account for this post is all virtual, mark it as // such, so that `handle_value' can show "(Account)" for accounts // that contain only virtual posts. post.reported_account()->xdata().add_flags(ACCOUNT_EXT_AUTO_VIRTUALIZE); if (! post.has_flags(POST_VIRTUAL)) post.reported_account()->xdata().add_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS); else if (! post.has_flags(POST_MUST_BALANCE)) post.reported_account()->xdata().add_flags(ACCOUNT_EXT_HAS_UNB_VIRTUALS); } void interval_posts::report_subtotal(const date_interval_t& ival) { if (exact_periods) subtotal_posts::report_subtotal(); else subtotal_posts::report_subtotal(NULL, ival); } namespace { struct sort_posts_by_date { bool operator()(post_t * left, post_t * right) const { return left->date() < right->date(); } }; } void interval_posts::operator()(post_t& post) { // If there is a duration (such as weekly), we must generate the // report in two passes. Otherwise, we only have to check whether the // post falls within the reporting period. if (interval.duration) { all_posts.push_back(&post); } else if (interval.find_period(post.date())) { item_handler<post_t>::operator()(post); } } void interval_posts::flush() { if (! interval.duration) { item_handler<post_t>::flush(); return; } // Sort all the postings we saw by date ascending std::stable_sort(all_posts.begin(), all_posts.end(), sort_posts_by_date()); // only if the interval has no start use the earliest post if (!(interval.begin() && interval.find_period(*interval.begin()))) // Determine the beginning interval by using the earliest post if (all_posts.size() > 0 && all_posts.front() && !interval.find_period(all_posts.front()->date())) throw_(std::logic_error, _("Failed to find period for interval report")); // Walk the interval forward reporting all posts within each one // before moving on, until we reach the end of all_posts bool saw_posts = false; for (std::deque<post_t *>::iterator i = all_posts.begin(); i != all_posts.end(); ) { post_t * post(*i); DEBUG("filters.interval", "Considering post " << post->date() << " = " << post->amount); #if DEBUG_ON DEBUG("filters.interval", "interval is:"); debug_interval(interval); #endif assert(! interval.finish || post->date() < *interval.finish); if (interval.within_period(post->date())) { DEBUG("filters.interval", "Calling subtotal_posts::operator()"); subtotal_posts::operator()(*post); ++i; saw_posts = true; } else { if (saw_posts) { DEBUG("filters.interval", "Calling subtotal_posts::report_subtotal()"); report_subtotal(interval); saw_posts = false; } else if (generate_empty_posts) { // Generate a null posting, so the intervening periods can be // seen when -E is used, or if the calculated amount ends up // being non-zero xact_t& null_xact = temps.create_xact(); null_xact._date = interval.inclusive_end(); post_t& null_post = temps.create_post(null_xact, empty_account); null_post.add_flags(POST_CALCULATED); null_post.amount = 0L; subtotal_posts::operator()(null_post); report_subtotal(interval); } DEBUG("filters.interval", "Advancing interval"); ++interval; } } // If the last postings weren't reported, do so now. if (saw_posts) { DEBUG("filters.interval", "Calling subtotal_posts::report_subtotal() at end"); report_subtotal(interval); } // Tell our parent class to flush subtotal_posts::flush(); } namespace { struct create_post_from_amount { post_handler_ptr handler; xact_t& xact; account_t& balance_account; temporaries_t& temps; explicit create_post_from_amount(post_handler_ptr _handler, xact_t& _xact, account_t& _balance_account, temporaries_t& _temps) : handler(_handler), xact(_xact), balance_account(_balance_account), temps(_temps) { TRACE_CTOR(create_post_from_amount, "post_handler_ptr, xact_t&, account_t&, temporaries_t&"); } create_post_from_amount(const create_post_from_amount& other) : handler(other.handler), xact(other.xact), balance_account(other.balance_account), temps(other.temps) { TRACE_CTOR(create_post_from_amount, "copy"); } ~create_post_from_amount() throw() { TRACE_DTOR(create_post_from_amount); } void operator()(const amount_t& amount) { post_t& balance_post = temps.create_post(xact, &balance_account); balance_post.amount = - amount; (*handler)(balance_post); } }; } void posts_as_equity::report_subtotal() { date_t finish; foreach (post_t * post, component_posts) { date_t date = post->date(); if (! is_valid(finish) || date > finish) finish = date; } component_posts.clear(); xact_t& xact = temps.create_xact(); xact.payee = _("Opening Balances"); xact._date = finish; value_t total = 0L; foreach (values_map::value_type& pair, values) { value_t value(pair.second.value.strip_annotations(report.what_to_keep())); if (unround) value.in_place_unround(); if (! value.is_zero()) { if (value.is_balance()) { value.as_balance_lval().map_sorted_amounts ([&](const amount_t& amt) { if (! amt.is_zero()) handle_value(/* value= */ amt, /* account= */ pair.second.account, /* xact= */ &xact, /* temps= */ temps, /* handler= */ handler, /* date= */ finish, /* act_date_p= */ false); }); } else { handle_value(/* value= */ value.to_amount(), /* account= */ pair.second.account, /* xact= */ &xact, /* temps= */ temps, /* handler= */ handler, /* date= */ finish, /* act_date_p= */ false); } } if (! pair.second.is_virtual || pair.second.must_balance) total += value; } values.clear(); // This last part isn't really needed, since an Equity:Opening // Balances posting with a null amount will automatically balance with // all the other postings generated. But it does make the full // balancing amount clearer to the user. if (! total.is_zero()) { create_post_from_amount post_creator(handler, xact, *balance_account, temps); if (total.is_balance()) total.as_balance_lval().map_sorted_amounts(post_creator); else post_creator(total.to_amount()); } } void by_payee_posts::flush() { foreach (payee_subtotals_map::value_type& pair, payee_subtotals) pair.second->report_subtotal(pair.first.c_str()); item_handler<post_t>::flush(); payee_subtotals.clear(); } void by_payee_posts::operator()(post_t& post) { payee_subtotals_map::iterator i = payee_subtotals.find(post.payee()); if (i == payee_subtotals.end()) { payee_subtotals_pair temp(post.payee(), shared_ptr<subtotal_posts>(new subtotal_posts(handler, amount_expr))); std::pair<payee_subtotals_map::iterator, bool> result = payee_subtotals.insert(temp); assert(result.second); if (! result.second) return; i = result.first; } (*(*i).second)(post); } void transfer_details::operator()(post_t& post) { xact_t& xact = temps.copy_xact(*post.xact); xact._date = post.date(); post_t& temp = temps.copy_post(post, xact); temp.set_state(post.state()); bind_scope_t bound_scope(scope, temp); value_t substitute(expr.calc(bound_scope)); if (! substitute.is_null()) { switch (which_element) { case SET_DATE: temp._date = substitute.to_date(); break; case SET_ACCOUNT: { string account_name = substitute.to_string(); if (! account_name.empty() && account_name[account_name.length() - 1] != ':') { account_t * prev_account = temp.account; temp.account->remove_post(&temp); account_name += ':'; account_name += prev_account->fullname(); std::list<string> account_names; split_string(account_name, ':', account_names); temp.account = create_temp_account_from_path(account_names, temps, xact.journal->master); temp.account->add_post(&temp); temp.account->add_flags(prev_account->flags()); if (prev_account->has_xdata()) temp.account->xdata().add_flags(prev_account->xdata().flags()); } break; } case SET_PAYEE: xact.payee = substitute.to_string(); break; } } item_handler<post_t>::operator()(temp); } void day_of_week_posts::flush() { for (int i = 0; i < 7; i++) { foreach (post_t * post, days_of_the_week[i]) subtotal_posts::operator()(*post); subtotal_posts::report_subtotal("%As"); days_of_the_week[i].clear(); } subtotal_posts::flush(); } void generate_posts::add_period_xacts(period_xacts_list& period_xacts) { foreach (period_xact_t * xact, period_xacts) foreach (post_t * post, xact->posts) add_post(xact->period, *post); } void generate_posts::add_post(const date_interval_t& period, post_t& post) { pending_posts.push_back(pending_posts_pair(period, &post)); } void budget_posts::report_budget_items(const date_t& date) { { // Cleanup pending items that finished before date // We have to keep them until the last day they apply because operator() needs them to see if a // posting is budgeted or not std::list<pending_posts_list::iterator> posts_to_erase; for (pending_posts_list::iterator i = pending_posts.begin(); i != pending_posts.end(); i++) { pending_posts_list::value_type& pair(*i); if (pair.first.finish && ! pair.first.start && pair.first.finish < date) { posts_to_erase.push_back(i); } } foreach (pending_posts_list::iterator& i, posts_to_erase) pending_posts.erase(i); } if (pending_posts.size() == 0) return; bool reported; do { reported = false; for (pending_posts_list::iterator i = pending_posts.begin(); i != pending_posts.end(); i++) { pending_posts_list::value_type& pair(*i); if (pair.first.finish && ! pair.first.start) continue; // skip expired posts optional<date_t> begin = pair.first.start; if (! begin) { optional<date_t> range_begin; if (pair.first.range) range_begin = pair.first.range->begin(); DEBUG("budget.generate", "Finding period for pending post"); if (! pair.first.find_period(range_begin ? *range_begin : date)) continue; if (! pair.first.start) throw_(std::logic_error, _("Failed to find period for periodic transaction")); begin = pair.first.start; } #if DEBUG_ON DEBUG("budget.generate", "begin = " << *begin); DEBUG("budget.generate", "date = " << date); if (pair.first.finish) DEBUG("budget.generate", "pair.first.finish = " << *pair.first.finish); #endif if (*begin <= date && (! pair.first.finish || *begin < *pair.first.finish)) { post_t& post = *pair.second; ++pair.first; DEBUG("budget.generate", "Reporting budget for " << post.reported_account()->fullname()); xact_t& xact = temps.create_xact(); xact.payee = _("Budget transaction"); xact._date = begin; post_t& temp = temps.copy_post(post, xact); temp.amount.in_place_negate(); if (flags & BUDGET_WRAP_VALUES) { value_t seq; seq.push_back(0L); seq.push_back(temp.amount); temp.xdata().compound_value = seq; temp.xdata().add_flags(POST_EXT_COMPOUND); } item_handler<post_t>::operator()(temp); reported = true; } } } while (reported); } void budget_posts::operator()(post_t& post) { bool post_in_budget = false; foreach (pending_posts_list::value_type& pair, pending_posts) { for (account_t * acct = post.reported_account(); acct; acct = acct->parent) { if (acct == (*pair.second).reported_account()) { post_in_budget = true; // Report the post as if it had occurred in the parent account. if (post.reported_account() != acct) post.set_reported_account(acct); goto handle; } } } handle: if (post_in_budget && flags & BUDGET_BUDGETED) { report_budget_items(post.date()); item_handler<post_t>::operator()(post); } else if (! post_in_budget && flags & BUDGET_UNBUDGETED) { item_handler<post_t>::operator()(post); } } void budget_posts::flush() { if (flags & BUDGET_BUDGETED) report_budget_items(terminus); item_handler<post_t>::flush(); } void forecast_posts::add_post(const date_interval_t& period, post_t& post) { date_interval_t i(period); if (! i.start && ! i.find_period(CURRENT_DATE())) return; generate_posts::add_post(i, post); // Advance the period's interval until it is at or beyond the current // date. while (*i.start < CURRENT_DATE()) ++i; } void forecast_posts::flush() { posts_list passed; date_t last = CURRENT_DATE(); // If there are period transactions to apply in a continuing series until // the forecast condition is met, generate those transactions now. Note // that no matter what, we abandon forecasting beyond the next 5 years. // // It works like this: // // Earlier, in forecast_posts::add_period_xacts, we cut up all the periodic // transactions into their components postings, so that we have N "periodic // postings". For example, if the user had this: // // ~ daily // Expenses:Food $10 // Expenses:Auto:Gas $20 // ~ monthly // Expenses:Food $100 // Expenses:Auto:Gas $200 // // We now have 4 periodic postings in `pending_posts'. // // Each periodic postings gets its own copy of its parent transaction's // period, which is modified as we go. This is found in the second member // of the pending_posts_list for each posting. // // The algorithm below works by iterating through the N periodic postings // over and over, until each of them meets the termination criteria for the // forecast and is removed from the set. while (pending_posts.size() > 0) { // At each step through the loop, we find the first periodic posting whose // period contains the earliest starting date. pending_posts_list::iterator least = pending_posts.begin(); for (pending_posts_list::iterator i = ++pending_posts.begin(); i != pending_posts.end(); i++) { assert((*i).first.start); assert((*least).first.start); if (*(*i).first.start < *(*least).first.start) least = i; } #if !NO_ASSERTS if ((*least).first.finish) assert(*(*least).first.start < *(*least).first.finish); #endif // If the next date in the series for this periodic posting is more than 5 // years beyond the last valid post we generated, drop it from further // consideration. date_t& next(*(*least).first.next); assert(next > *(*least).first.start); if (static_cast<std::size_t>((next - last).days()) > static_cast<std::size_t>(365U) * forecast_years) { DEBUG("filters.forecast", "Forecast transaction exceeds " << forecast_years << " years beyond today"); pending_posts.erase(least); continue; } // `post' refers to the posting defined in the period transaction. We // make a copy of it within a temporary transaction with the payee // "Forecast transaction". post_t& post = *(*least).second; xact_t& xact = temps.create_xact(); xact.payee = _("Forecast transaction"); xact._date = next; post_t& temp = temps.copy_post(post, xact); // Submit the generated posting DEBUG("filters.forecast", "Forecast transaction: " << temp.date() << " " << temp.account->fullname() << " " << temp.amount); item_handler<post_t>::operator()(temp); // If the generated posting matches the user's report query, check whether // it also fails to match the continuation condition for the forecast. If // it does, drop this periodic posting from consideration. if (temp.has_xdata() && temp.xdata().has_flags(POST_EXT_MATCHES)) { DEBUG("filters.forecast", " matches report query"); bind_scope_t bound_scope(context, temp); if (! pred(bound_scope)) { DEBUG("filters.forecast", " fails to match continuation criteria"); pending_posts.erase(least); continue; } } // Increment the 'least', but remove it from pending_posts if it // exceeds its own boundaries. ++(*least).first; if (! (*least).first.start) { pending_posts.erase(least); continue; } } item_handler<post_t>::flush(); } inject_posts::inject_posts(post_handler_ptr handler, const string& tag_list, account_t * master) : item_handler<post_t>(handler) { scoped_array<char> buf(new char[tag_list.length() + 1]); std::strcpy(buf.get(), tag_list.c_str()); for (char * q = std::strtok(buf.get(), ","); q; q = std::strtok(NULL, ",")) { std::list<string> account_names; split_string(q, ':', account_names); account_t * account = create_temp_account_from_path(account_names, temps, master); account->add_flags(ACCOUNT_GENERATED); tags_list.push_back (tags_list_pair(q, tag_mapping_pair(account, tag_injected_set()))); } TRACE_CTOR(inject_posts, "post_handler_ptr, string, account_t *"); } void inject_posts::operator()(post_t& post) { foreach (tags_list_pair& pair, tags_list) { optional<value_t> tag_value = post.get_tag(pair.first, false); // When checking if the transaction has the tag, only inject once // per transaction. if (! tag_value && pair.second.second.find(post.xact) == pair.second.second.end() && (tag_value = post.xact->get_tag(pair.first))) pair.second.second.insert(post.xact); if (tag_value) { xact_t& xact = temps.copy_xact(*post.xact); xact._date = post.date(); xact.add_flags(ITEM_GENERATED); post_t& temp = temps.copy_post(post, xact); temp.account = pair.second.first; temp.amount = tag_value->to_amount(); temp.add_flags(ITEM_GENERATED); item_handler<post_t>::operator()(temp); } } item_handler<post_t>::operator()(post); } } // namespace ledger �������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/filters.h��������������������������������������������������������������������������0000664�0000000�0000000�00000066112�14411236400�0015372�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup report */ /** * @file filters.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_FILTERS_H #define INCLUDED_FILTERS_H #include "chain.h" #include "xact.h" #include "post.h" #include "account.h" #include "temps.h" namespace ledger { ////////////////////////////////////////////////////////////////////// // // Posting collector // class post_splitter : public item_handler<post_t> { public: typedef std::map<value_t, posts_list> value_to_posts_map; typedef function<void (const value_t&)> custom_flusher_t; protected: value_to_posts_map posts_map; post_handler_ptr post_chain; report_t& report; expr_t& group_by_expr; custom_flusher_t preflush_func; optional<custom_flusher_t> postflush_func; public: post_splitter(post_handler_ptr _post_chain, report_t& _report, expr_t& _group_by_expr) : post_chain(_post_chain), report(_report), group_by_expr(_group_by_expr) { preflush_func = bind(&post_splitter::print_title, this, _1); TRACE_CTOR(post_splitter, "scope_t&, post_handler_ptr, expr_t"); } virtual ~post_splitter() { TRACE_DTOR(post_splitter); } void set_preflush_func(custom_flusher_t functor) { preflush_func = functor; } void set_postflush_func(custom_flusher_t functor) { postflush_func = functor; } virtual void print_title(const value_t& val); virtual void flush(); virtual void operator()(post_t& post); virtual void clear() { posts_map.clear(); post_chain->clear(); item_handler<post_t>::clear(); } }; ////////////////////////////////////////////////////////////////////// // // Posting filters // class ignore_posts : public item_handler<post_t> { public: virtual void operator()(post_t&) {} }; class collect_posts : public item_handler<post_t> { public: std::vector<post_t *> posts; collect_posts() : item_handler<post_t>() { TRACE_CTOR(collect_posts, ""); } virtual ~collect_posts() { TRACE_DTOR(collect_posts); } std::size_t length() const { return posts.size(); } std::vector<post_t *>::iterator begin() { return posts.begin(); } std::vector<post_t *>::iterator end() { return posts.end(); } virtual void flush() {} virtual void operator()(post_t& post) { posts.push_back(&post); } virtual void clear() { posts.clear(); item_handler<post_t>::clear(); } }; template <typename Iterator> class pass_down_posts : public item_handler<post_t> { pass_down_posts(); public: pass_down_posts(post_handler_ptr handler, Iterator& iter) : item_handler<post_t>(handler) { while (post_t * post = *iter) { try { item_handler<post_t>::operator()(*post); } catch (const std::exception&) { add_error_context(item_context(*post, _("While handling posting"))); throw; } iter.increment(); } item_handler<post_t>::flush(); TRACE_CTOR(pass_down_posts, "post_handler_ptr, posts_iterator"); } virtual ~pass_down_posts() { TRACE_DTOR(pass_down_posts); } }; class push_to_posts_list : public item_handler<post_t> { push_to_posts_list(); public: posts_list& posts; push_to_posts_list(posts_list& _posts) : posts(_posts) { TRACE_CTOR(push_to_posts_list, "posts_list&"); } virtual ~push_to_posts_list() { TRACE_DTOR(push_to_posts_list); } virtual void operator()(post_t& post) { posts.push_back(&post); } }; class truncate_xacts : public item_handler<post_t> { int head_count; int tail_count; bool completed; posts_list posts; std::size_t xacts_seen; xact_t * last_xact; truncate_xacts(); public: truncate_xacts(post_handler_ptr handler, int _head_count, int _tail_count) : item_handler<post_t>(handler), head_count(_head_count), tail_count(_tail_count), completed(false), xacts_seen(0), last_xact(NULL) { TRACE_CTOR(truncate_xacts, "post_handler_ptr, int, int"); } virtual ~truncate_xacts() { TRACE_DTOR(truncate_xacts); } virtual void flush(); virtual void operator()(post_t& post); virtual void clear() { completed = false; posts.clear(); xacts_seen = 0; last_xact = NULL; item_handler<post_t>::clear(); } }; class sort_posts : public item_handler<post_t> { typedef std::deque<post_t *> posts_deque; posts_deque posts; expr_t sort_order; report_t& report; sort_posts(); public: sort_posts(post_handler_ptr handler, const expr_t& _sort_order, report_t& _report) : item_handler<post_t>(handler), sort_order(_sort_order), report(_report) { TRACE_CTOR(sort_posts, "post_handler_ptr, const value_expr&, report_t&"); } sort_posts(post_handler_ptr handler, const string& _sort_order, report_t& _report) : item_handler<post_t>(handler), sort_order(_sort_order), report(_report) { TRACE_CTOR(sort_posts, "post_handler_ptr, const string&, report_t&"); } virtual ~sort_posts() { TRACE_DTOR(sort_posts); } virtual void post_accumulated_posts(); virtual void flush() { post_accumulated_posts(); item_handler<post_t>::flush(); } virtual void operator()(post_t& post) { posts.push_back(&post); } virtual void clear() { posts.clear(); sort_order.mark_uncompiled(); item_handler<post_t>::clear(); } }; class sort_xacts : public item_handler<post_t> { sort_posts sorter; xact_t * last_xact; sort_xacts(); public: sort_xacts(post_handler_ptr handler, const expr_t& _sort_order, report_t& _report) : sorter(handler, _sort_order, _report) { TRACE_CTOR(sort_xacts, "post_handler_ptr, const value_expr&, report_t&"); } sort_xacts(post_handler_ptr handler, const string& _sort_order, report_t& _report) : sorter(handler, _sort_order, _report) { TRACE_CTOR(sort_xacts, "post_handler_ptr, const string&, report_t&"); } virtual ~sort_xacts() { TRACE_DTOR(sort_xacts); } virtual void flush() { sorter.flush(); item_handler<post_t>::flush(); } virtual void operator()(post_t& post) { if (last_xact && post.xact != last_xact) sorter.post_accumulated_posts(); sorter(post); last_xact = post.xact; } virtual void clear() { sorter.clear(); last_xact = NULL; item_handler<post_t>::clear(); } }; class filter_posts : public item_handler<post_t> { predicate_t pred; scope_t& context; filter_posts(); public: filter_posts(post_handler_ptr handler, const predicate_t& predicate, scope_t& _context) : item_handler<post_t>(handler), pred(predicate), context(_context) { TRACE_CTOR(filter_posts, "post_handler_ptr, predicate_t, scope_t&"); } virtual ~filter_posts() { TRACE_DTOR(filter_posts); } virtual void operator()(post_t& post) { bind_scope_t bound_scope(context, post); if (pred(bound_scope)) { post.xdata().add_flags(POST_EXT_MATCHES); (*handler)(post); } } virtual void clear() { pred.mark_uncompiled(); item_handler<post_t>::clear(); } }; class anonymize_posts : public item_handler<post_t> { typedef std::map<commodity_t *, std::size_t> commodity_index_map; typedef variate_generator<mt19937&, uniform_int<> > int_generator_t; temporaries_t temps; commodity_index_map comms; std::size_t next_comm_id; xact_t * last_xact; mt19937 rnd_gen; uniform_int<> integer_range; int_generator_t integer_gen; anonymize_posts(); public: anonymize_posts(post_handler_ptr handler) : item_handler<post_t>(handler), next_comm_id(0), last_xact(NULL), rnd_gen(static_cast<unsigned int>(static_cast<boost::uintmax_t>(std::time(0)))), integer_range(1, 2000000000L), integer_gen(rnd_gen, integer_range) { TRACE_CTOR(anonymize_posts, "post_handler_ptr"); } virtual ~anonymize_posts() { TRACE_DTOR(anonymize_posts); handler.reset(); } void render_commodity(amount_t& amt); virtual void operator()(post_t& post); virtual void clear() { temps.clear(); comms.clear(); last_xact = NULL; item_handler<post_t>::clear(); } }; class calc_posts : public item_handler<post_t> { post_t * last_post; expr_t& amount_expr; bool calc_running_total; calc_posts(); public: calc_posts(post_handler_ptr handler, expr_t& _amount_expr, bool _calc_running_total = false) : item_handler<post_t>(handler), last_post(NULL), amount_expr(_amount_expr), calc_running_total(_calc_running_total) { TRACE_CTOR(calc_posts, "post_handler_ptr, expr_t&, bool"); } virtual ~calc_posts() { TRACE_DTOR(calc_posts); } virtual void operator()(post_t& post); virtual void clear() { last_post = NULL; amount_expr.mark_uncompiled(); item_handler<post_t>::clear(); } }; class collapse_posts : public item_handler<post_t> { typedef std::map<account_t *,value_t> totals_map; expr_t& amount_expr; predicate_t display_predicate; predicate_t only_predicate; value_t subtotal; std::size_t count; xact_t * last_xact; post_t * last_post; temporaries_t temps; account_t * global_totals_account; totals_map totals; bool only_collapse_if_zero; unsigned short collapse_depth; std::list<post_t *> component_posts; report_t& report; collapse_posts(); public: collapse_posts(post_handler_ptr handler, report_t& _report, expr_t& _amount_expr, predicate_t _display_predicate, predicate_t _only_predicate, bool _only_collapse_if_zero = false, unsigned short _collapse_depth = 0) : item_handler<post_t>(handler), amount_expr(_amount_expr), display_predicate(_display_predicate), only_predicate(_only_predicate), count(0), last_xact(NULL), last_post(NULL), only_collapse_if_zero(_only_collapse_if_zero), collapse_depth(_collapse_depth), report(_report) { create_accounts(); TRACE_CTOR(collapse_posts, "post_handler_ptr, ..."); } virtual ~collapse_posts() { TRACE_DTOR(collapse_posts); handler.reset(); } void create_accounts() { global_totals_account = &temps.create_account(_("<Total>")); } value_t& find_totals(account_t* account); virtual void flush() { report_subtotal(); item_handler<post_t>::flush(); } void report_subtotal(); virtual void operator()(post_t& post); virtual void clear() { amount_expr.mark_uncompiled(); display_predicate.mark_uncompiled(); only_predicate.mark_uncompiled(); subtotal = value_t(); count = 0; last_xact = NULL; last_post = NULL; temps.clear(); create_accounts(); totals.clear(); component_posts.clear(); item_handler<post_t>::clear(); } }; class related_posts : public item_handler<post_t> { posts_list posts; bool also_matching; related_posts(); public: related_posts(post_handler_ptr handler, const bool _also_matching = false) : item_handler<post_t>(handler), also_matching(_also_matching) { TRACE_CTOR(related_posts, "post_handler_ptr, const bool"); } virtual ~related_posts() throw() { TRACE_DTOR(related_posts); } virtual void flush(); virtual void operator()(post_t& post) { post.xdata().add_flags(POST_EXT_RECEIVED); posts.push_back(&post); } virtual void clear() { posts.clear(); item_handler<post_t>::clear(); } }; class display_filter_posts : public item_handler<post_t> { // This filter requires that calc_posts be used at some point // later in the chain. report_t& report; expr_t& display_amount_expr; expr_t& display_total_expr; bool show_rounding; value_t last_display_total; temporaries_t temps; account_t * rounding_account; display_filter_posts(); public: account_t * revalued_account; display_filter_posts(post_handler_ptr handler, report_t& _report, bool _show_rounding); virtual ~display_filter_posts() { TRACE_DTOR(display_filter_posts); handler.reset(); } void create_accounts() { rounding_account = &temps.create_account(_("<Adjustment>")); revalued_account = &temps.create_account(_("<Revalued>")); } bool output_rounding(post_t& post); virtual void operator()(post_t& post); virtual void clear() { display_amount_expr.mark_uncompiled(); display_total_expr.mark_uncompiled(); last_display_total = value_t(); temps.clear(); item_handler<post_t>::clear(); create_accounts(); } }; class changed_value_posts : public item_handler<post_t> { // This filter requires that calc_posts be used at some point // later in the chain. report_t& report; expr_t& total_expr; expr_t& display_total_expr; bool changed_values_only; bool historical_prices_only; bool for_accounts_report; bool show_unrealized; post_t * last_post; value_t last_total; value_t repriced_total; temporaries_t temps; account_t * revalued_account; account_t * gains_equity_account; account_t * losses_equity_account; display_filter_posts * display_filter; changed_value_posts(); public: changed_value_posts(post_handler_ptr handler, report_t& _report, bool _for_accounts_report, bool _show_unrealized, display_filter_posts * _display_filter); virtual ~changed_value_posts() { TRACE_DTOR(changed_value_posts); temps.clear(); handler.reset(); } void create_accounts() { revalued_account = (display_filter ? display_filter->revalued_account : &temps.create_account(_("<Revalued>"))); } virtual void flush(); void output_revaluation(post_t& post, const date_t& current); void output_intermediate_prices(post_t& post, const date_t& current); virtual void operator()(post_t& post); virtual void clear() { total_expr.mark_uncompiled(); display_total_expr.mark_uncompiled(); last_post = NULL; last_total = value_t(); temps.clear(); item_handler<post_t>::clear(); create_accounts(); } }; class subtotal_posts : public item_handler<post_t> { subtotal_posts(); protected: class acct_value_t { acct_value_t(); public: account_t * account; value_t value; bool is_virtual; bool must_balance; acct_value_t(account_t * a, bool _is_virtual = false, bool _must_balance = false) : account(a), is_virtual(_is_virtual), must_balance(_must_balance) { TRACE_CTOR(acct_value_t, "account_t *, bool, bool"); } acct_value_t(account_t * a, value_t& v, bool _is_virtual = false, bool _must_balance = false) : account(a), value(v), is_virtual(_is_virtual), must_balance(_must_balance) { TRACE_CTOR(acct_value_t, "account_t *, value_t&, bool, bool"); } acct_value_t(const acct_value_t& av) : account(av.account), value(av.value), is_virtual(av.is_virtual), must_balance(av.must_balance) { TRACE_CTOR(acct_value_t, "copy"); } ~acct_value_t() throw() { TRACE_DTOR(acct_value_t); } }; typedef std::map<string, acct_value_t> values_map; typedef std::pair<string, acct_value_t> values_pair; protected: expr_t& amount_expr; values_map values; optional<string> date_format; temporaries_t temps; std::deque<post_t *> component_posts; public: subtotal_posts(post_handler_ptr handler, expr_t& _amount_expr, const optional<string>& _date_format = none) : item_handler<post_t>(handler), amount_expr(_amount_expr), date_format(_date_format) { TRACE_CTOR(subtotal_posts, "post_handler_ptr, expr_t&, const optional<string>&"); } virtual ~subtotal_posts() { TRACE_DTOR(subtotal_posts); handler.reset(); } void report_subtotal(const char * spec_fmt = NULL, const optional<date_interval_t>& interval = none); virtual void flush() { if (values.size() > 0) report_subtotal(); item_handler<post_t>::flush(); } virtual void operator()(post_t& post); virtual void clear() { amount_expr.mark_uncompiled(); values.clear(); temps.clear(); component_posts.clear(); item_handler<post_t>::clear(); } }; class interval_posts : public subtotal_posts { date_interval_t start_interval; date_interval_t interval; account_t * empty_account; bool exact_periods; bool generate_empty_posts; std::deque<post_t *> all_posts; interval_posts(); public: interval_posts(post_handler_ptr _handler, expr_t& amount_expr, const date_interval_t& _interval, bool _exact_periods = false, bool _generate_empty_posts = false) : subtotal_posts(_handler, amount_expr), start_interval(_interval), interval(start_interval), exact_periods(_exact_periods), generate_empty_posts(_generate_empty_posts) { create_accounts(); TRACE_CTOR(interval_posts, "post_handler_ptr, expr_t&, date_interval_t, bool, bool"); } virtual ~interval_posts() throw() { TRACE_DTOR(interval_posts); } void create_accounts() { empty_account = &temps.create_account(_("<None>")); } void report_subtotal(const date_interval_t& ival); #if DEBUG_ON void debug_interval(const date_interval_t& ival) { if (ival.start) DEBUG("filters.interval", "start = " << *ival.start); else DEBUG("filters.interval", "no start"); if (ival.finish) DEBUG("filters.interval", "finish = " << *ival.finish); else DEBUG("filters.interval", "no finish"); } #endif virtual void operator()(post_t& post); virtual void flush(); virtual void clear() { interval = start_interval; subtotal_posts::clear(); create_accounts(); } }; class posts_as_equity : public subtotal_posts { report_t& report; post_t * last_post; account_t * equity_account; account_t * balance_account; bool unround; posts_as_equity(); public: posts_as_equity(post_handler_ptr _handler, report_t& _report, expr_t& amount_expr, bool _unround) : subtotal_posts(_handler, amount_expr), report(_report), unround(_unround) { create_accounts(); TRACE_CTOR(posts_as_equity, "post_handler_ptr, expr_t&"); } virtual ~posts_as_equity() throw() { TRACE_DTOR(posts_as_equity); } void create_accounts() { equity_account = &temps.create_account(_("Equity")); balance_account = equity_account->find_account(_("Opening Balances")); } void report_subtotal(); virtual void flush() { report_subtotal(); subtotal_posts::flush(); } virtual void clear() { last_post = NULL; subtotal_posts::clear(); create_accounts(); } }; class by_payee_posts : public item_handler<post_t> { typedef std::map<string, shared_ptr<subtotal_posts> > payee_subtotals_map; typedef std::pair<string, shared_ptr<subtotal_posts> > payee_subtotals_pair; expr_t& amount_expr; payee_subtotals_map payee_subtotals; by_payee_posts(); public: by_payee_posts(post_handler_ptr handler, expr_t& _amount_expr) : item_handler<post_t>(handler), amount_expr(_amount_expr) { TRACE_CTOR(by_payee_posts, "post_handler_ptr, expr_t&"); } virtual ~by_payee_posts() { TRACE_DTOR(by_payee_posts); } virtual void flush(); virtual void operator()(post_t& post); virtual void clear() { amount_expr.mark_uncompiled(); payee_subtotals.clear(); item_handler<post_t>::clear(); } }; class transfer_details : public item_handler<post_t> { account_t * master; expr_t expr; scope_t& scope; temporaries_t temps; transfer_details(); public: enum element_t { SET_DATE, SET_ACCOUNT, SET_PAYEE } which_element; transfer_details(post_handler_ptr handler, element_t _which_element, account_t * _master, const expr_t& _expr, scope_t& _scope) : item_handler<post_t>(handler), master(_master), expr(_expr), scope(_scope), which_element(_which_element) { TRACE_CTOR(transfer_details, "post_handler_ptr, element_t, account_t *, expr_t, scope_t&"); } virtual ~transfer_details() { TRACE_DTOR(transfer_details); handler.reset(); } virtual void operator()(post_t& post); virtual void clear() { expr.mark_uncompiled(); temps.clear(); item_handler<post_t>::clear(); } }; class day_of_week_posts : public subtotal_posts { posts_list days_of_the_week[7]; day_of_week_posts(); public: day_of_week_posts(post_handler_ptr handler, expr_t& amount_expr) : subtotal_posts(handler, amount_expr) { TRACE_CTOR(day_of_week_posts, "post_handler_ptr, bool"); } virtual ~day_of_week_posts() throw() { TRACE_DTOR(day_of_week_posts); } virtual void flush(); virtual void operator()(post_t& post) { days_of_the_week[post.date().day_of_week()].push_back(&post); } virtual void clear() { for (int i = 0; i < 7; i++) days_of_the_week[i].clear(); subtotal_posts::clear(); } }; class generate_posts : public item_handler<post_t> { generate_posts(); protected: typedef std::pair<date_interval_t, post_t *> pending_posts_pair; typedef std::list<pending_posts_pair> pending_posts_list; pending_posts_list pending_posts; temporaries_t temps; public: generate_posts(post_handler_ptr handler) : item_handler<post_t>(handler) { TRACE_CTOR(generate_posts, "post_handler_ptr"); } virtual ~generate_posts() { TRACE_DTOR(generate_posts); handler.reset(); } void add_period_xacts(period_xacts_list& period_xacts); virtual void add_post(const date_interval_t& period, post_t& post); virtual void clear() { pending_posts.clear(); temps.clear(); item_handler<post_t>::clear(); } }; class budget_posts : public generate_posts { #define BUDGET_NO_BUDGET 0x00 #define BUDGET_BUDGETED 0x01 #define BUDGET_UNBUDGETED 0x02 #define BUDGET_WRAP_VALUES 0x04 uint_least8_t flags; date_t terminus; budget_posts(); public: budget_posts(post_handler_ptr handler, date_t _terminus, uint_least8_t _flags = BUDGET_BUDGETED) : generate_posts(handler), flags(_flags), terminus(_terminus) { TRACE_CTOR(budget_posts, "post_handler_ptr, date_t, uint_least8_t"); } virtual ~budget_posts() throw() { TRACE_DTOR(budget_posts); } void report_budget_items(const date_t& date); virtual void flush(); virtual void operator()(post_t& post); }; class forecast_posts : public generate_posts { predicate_t pred; scope_t& context; const std::size_t forecast_years; public: forecast_posts(post_handler_ptr handler, const predicate_t& predicate, scope_t& _context, const std::size_t _forecast_years) : generate_posts(handler), pred(predicate), context(_context), forecast_years(_forecast_years) { TRACE_CTOR(forecast_posts, "post_handler_ptr, predicate_t, scope_t&, std::size_t"); } virtual ~forecast_posts() throw() { TRACE_DTOR(forecast_posts); } virtual void add_post(const date_interval_t& period, post_t& post); virtual void flush(); virtual void clear() { pred.mark_uncompiled(); generate_posts::clear(); } }; class inject_posts : public item_handler<post_t> { typedef std::set<xact_t *> tag_injected_set; typedef std::pair<account_t *, tag_injected_set> tag_mapping_pair; typedef std::pair<string, tag_mapping_pair> tags_list_pair; std::list<tags_list_pair> tags_list; temporaries_t temps; public: inject_posts(post_handler_ptr handler, const string& tag_list, account_t * master); virtual ~inject_posts() throw() { TRACE_DTOR(inject_posts); handler.reset(); } virtual void operator()(post_t& post); }; ////////////////////////////////////////////////////////////////////// // // Account filters // template <typename Iterator> class pass_down_accounts : public item_handler<account_t> { pass_down_accounts(); optional<predicate_t> pred; optional<scope_t&> context; public: pass_down_accounts(acct_handler_ptr handler, Iterator& iter, const optional<predicate_t>& _pred = none, const optional<scope_t&>& _context = none) : item_handler<account_t>(handler), pred(_pred), context(_context) { TRACE_CTOR(pass_down_accounts, "acct_handler_ptr, accounts_iterator, ..."); while (account_t * account = *iter++) { if (! pred) { item_handler<account_t>::operator()(*account); } else { bind_scope_t bound_scope(*context, *account); if ((*pred)(bound_scope)) item_handler<account_t>::operator()(*account); } } item_handler<account_t>::flush(); } virtual ~pass_down_accounts() { TRACE_DTOR(pass_down_accounts); } virtual void clear() { if (pred) pred->mark_uncompiled(); item_handler<account_t>::clear(); } }; } // namespace ledger #endif // INCLUDED_FILTERS_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/flags.h����������������������������������������������������������������������������0000664�0000000�0000000�00000011523�14411236400�0015012�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup util */ /** * @file flags.h * @author John Wiegley * * @ingroup util */ #ifndef INCLUDED_FLAGS_H #define INCLUDED_FLAGS_H template <typename T = boost::uint_least8_t, typename U = T> class supports_flags { public: typedef T flags_t; protected: flags_t _flags; public: supports_flags() : _flags(static_cast<T>(0)) { TRACE_CTOR(supports_flags, ""); } supports_flags(const supports_flags& arg) : _flags(arg._flags) { TRACE_CTOR(supports_flags, "copy"); } supports_flags(const flags_t& arg) : _flags(arg) { TRACE_CTOR(supports_flags, "const flags_t&"); } ~supports_flags() throw() { TRACE_DTOR(supports_flags); } supports_flags& operator=(const supports_flags& other) { _flags = other._flags; return *this; } flags_t flags() const { return _flags; } bool has_flags(const flags_t arg) const { return _flags & arg; } void set_flags(const flags_t arg) { _flags = arg; } void clear_flags() { _flags = static_cast<T>(0); } void add_flags(const flags_t arg) { _flags = static_cast<T>(static_cast<U>(_flags) | static_cast<U>(arg)); } void drop_flags(const flags_t arg) { _flags = static_cast<T>(static_cast<U>(_flags) & static_cast<U>(~arg)); } }; template <typename T = boost::uint_least8_t, typename U = T> class basic_flags_t : public supports_flags<T, U> { public: basic_flags_t() { TRACE_CTOR(basic_flags_t, ""); } basic_flags_t(const T& bits) { TRACE_CTOR(basic_flags_t, "const T&"); supports_flags<T, U>::set_flags(bits); } basic_flags_t(const U& bits) { TRACE_CTOR(basic_flags_t, "const U&"); supports_flags<T, U>::set_flags(static_cast<T>(bits)); } ~basic_flags_t() throw() { TRACE_DTOR(basic_flags_t); } basic_flags_t(const basic_flags_t& other) : supports_flags<T, U>(other) { TRACE_CTOR(basic_flags_t, "copy"); } basic_flags_t& operator=(const basic_flags_t& other) { set_flags(other.flags()); return *this; } basic_flags_t& operator=(const T& bits) { set_flags(bits); return *this; } operator T() const { return supports_flags<T, U>::flags(); } operator U() const { return supports_flags<T, U>::flags(); } basic_flags_t plus_flags(const T& arg) const { basic_flags_t temp(*this); temp.add_flags(arg); return temp; } basic_flags_t minus_flags(const T& arg) const { basic_flags_t temp(*this); temp.drop_flags(arg); return temp; } }; template <typename T = boost::uint_least8_t> class delegates_flags : public boost::noncopyable { public: typedef T flags_t; protected: supports_flags<T>& _flags; public: delegates_flags() : _flags() { TRACE_CTOR(delegates_flags, ""); } delegates_flags(supports_flags<T>& arg) : _flags(arg) { TRACE_CTOR(delegates_flags, "const supports_flags<T>&"); } ~delegates_flags() throw() { TRACE_DTOR(delegates_flags); } flags_t flags() const { return _flags.flags(); } bool has_flags(const flags_t arg) const { return _flags.has_flags(arg); } void set_flags(const flags_t arg) { _flags.set_flags(arg); } void clear_flags() { _flags.clear_flags(); } void add_flags(const flags_t arg) { _flags.add_flags(arg); } void drop_flags(const flags_t arg) { _flags.drop_flags(arg); } }; #endif // INCLUDED_FLAGS_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/format.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000054140�14411236400�0015346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "format.h" #include "scope.h" #include "pstream.h" namespace ledger { format_t::elision_style_t format_t::default_style = TRUNCATE_TRAILING; bool format_t::default_style_changed = false; void format_t::element_t::dump(std::ostream& out) const { out << "Element: "; switch (type) { case STRING: out << " STRING"; break; case EXPR: out << " EXPR"; break; } out << " flags: 0x" << std::hex << int(flags()); out << " min: "; out << std::right; out.width(2); out << std::dec << int(min_width); out << " max: "; out << std::right; out.width(2); out << std::dec << int(max_width); switch (type) { case STRING: out << " str: '" << boost::get<string>(data) << "'" << std::endl; break; case EXPR: out << " expr: " << boost::get<expr_t>(data) << std::endl; break; } } namespace { struct format_mapping_t { char letter; const char * expr; } single_letter_mappings[] = { { 'd', "aux_date ? format_date(date) + \"=\" + format_date(aux_date) : format_date(date)" }, { 'D', "date" }, { 'S', "filename" }, { 'B', "beg_pos" }, { 'b', "beg_line" }, { 'E', "end_pos" }, { 'e', "end_line" }, { 'X', "\"* \" if cleared" }, { 'Y', "\"* \" if xact.cleared" }, { 'C', "\"(\" + code + \") \" if code" }, { 'P', "payee" }, { 'a', "account" }, { 'A', "account" }, { 't', "justify(scrub(display_amount), $min, $max, $left, color)" }, { 'T', "justify(scrub(display_total), $min, $max, $left, color)" }, { 'N', "note" }, }; expr_t parse_single_expression(const char *& p, bool single_expr = true) { string temp(p); ptristream str(const_cast<char *&>(p)); expr_t expr; expr.parse(str, single_expr ? PARSE_SINGLE : PARSE_PARTIAL, temp); if (str.eof()) { expr.set_text(p); p += std::strlen(p); } else { assert(str.good()); std::istream::pos_type pos = str.tellg(); expr.set_text(string(p, p + long(pos))); p += long(pos) - 1; // Don't gobble up any whitespace const char * base = p; while (p >= base && std::isspace(*p)) p--; } return expr; } inline expr_t::ptr_op_t ident_node(const string& ident) { expr_t::ptr_op_t node(new expr_t::op_t(expr_t::op_t::IDENT)); node->set_ident(ident); return node; } } format_t::element_t * format_t::parse_elements(const string& fmt, const optional<format_t&>& tmpl) { unique_ptr<element_t> result; element_t * current = NULL; static char buf[65535]; char * q = buf; for (const char * p = fmt.c_str(); *p; p++) { if (*p != '%' && *p != '\\') { *q++ = *p; continue; } if (! result.get()) { result.reset(new element_t); current = result.get(); } else { current->next.reset(new element_t); current = current->next.get(); } if (q != buf) { current->type = element_t::STRING; current->data = string(buf, q); q = buf; current->next.reset(new element_t); current = current->next.get(); } if (*p == '\\') { p++; current->type = element_t::STRING; switch (*p) { case 'b': current->data = string("\b"); break; case 'f': current->data = string("\f"); break; case 'n': current->data = string("\n"); break; case 'r': current->data = string("\r"); break; case 't': current->data = string("\t"); break; case 'v': current->data = string("\v"); break; case '\\': current->data = string("\\"); break; default: current->data = string(1, *p); break; } continue; } ++p; while (*p == '-') { switch (*p) { case '-': current->add_flags(ELEMENT_ALIGN_LEFT); break; } ++p; } std::size_t num = 0; while (*p && std::isdigit(*p)) { num *= 10; num += static_cast<std::size_t>(*p++ - '0'); } current->min_width = num; if (*p == '.') { ++p; num = 0; while (*p && std::isdigit(*p)) { num *= 10; num += static_cast<std::size_t>(*p++ - '0'); } current->max_width = num; if (current->min_width == 0) current->min_width = current->max_width; } if (std::isalpha(*p)) { bool found = false; for (std::size_t i = 0; i < (sizeof(single_letter_mappings) / sizeof(format_mapping_t)); i++) { if (*p == single_letter_mappings[i].letter) { std::ostringstream expr; for (const char * ptr = single_letter_mappings[i].expr; *ptr;) { if (*ptr == '$') { const char * beg = ++ptr; while (*ptr && std::isalpha(*ptr)) ++ptr; string::size_type klen = static_cast<string::size_type>(ptr - beg); string keyword(beg, 0, klen); if (keyword == "min") expr << (current->min_width > 0 ? static_cast<int>(current->min_width) : -1); else if (keyword == "max") expr << (current->max_width > 0 ? static_cast<int>(current->max_width) : -1); else if (keyword == "left") expr << (current->has_flags(ELEMENT_ALIGN_LEFT) ? "false" : "true"); #if DEBUG_ON else assert("Unrecognized format substitution keyword" == NULL); #endif } else { expr << *ptr++; } } current->type = element_t::EXPR; current->data = expr_t(expr.str()); found = true; break; } } if (! found) throw_(format_error, _f("Unrecognized formatting character: %1%") % *p); } else { switch (*p) { case '%': current->type = element_t::STRING; current->data = string("%"); break; case '$': { if (! tmpl) throw_(format_error, _("Prior field reference, but no template")); p++; if (*p == '0' || (! std::isdigit(*p) && *p != 'A' && *p != 'B' && *p != 'C' && *p != 'D' && *p != 'E' && *p != 'F')) throw_(format_error, _("%$ field reference must be a digit from 1-9")); int index = std::isdigit(*p) ? *p - '0' : (*p - 'A' + 10); element_t * tmpl_elem = tmpl->elements.get(); for (int i = 1; i < index && tmpl_elem; i++) { tmpl_elem = tmpl_elem->next.get(); while (tmpl_elem && tmpl_elem->type != element_t::EXPR) tmpl_elem = tmpl_elem->next.get(); } if (! tmpl_elem) throw_(format_error, _("%$ reference to a non-existent prior field")); *current = *tmpl_elem; break; } case '(': case '{': { bool format_amount = *p == '{'; current->type = element_t::EXPR; current->data = parse_single_expression(p); // Wrap the subexpression in calls to justify and scrub if (! format_amount) break; expr_t::ptr_op_t op = boost::get<expr_t>(current->data).get_op(); expr_t::ptr_op_t call2_node(new expr_t::op_t(expr_t::op_t::O_CALL)); { call2_node->set_left(ident_node("justify")); { expr_t::ptr_op_t args3_node(new expr_t::op_t(expr_t::op_t::O_CONS)); { { expr_t::ptr_op_t call1_node(new expr_t::op_t(expr_t::op_t::O_CALL)); { call1_node->set_left(ident_node("scrub")); call1_node->set_right(op->kind == expr_t::op_t::O_CONS ? op->left() : op); } args3_node->set_left(call1_node); } expr_t::ptr_op_t args2_node(new expr_t::op_t(expr_t::op_t::O_CONS)); { { expr_t::ptr_op_t arg1_node(new expr_t::op_t(expr_t::op_t::VALUE)); arg1_node->set_value(current->min_width > 0 ? long(current->min_width) : -1); args2_node->set_left(arg1_node); } { expr_t::ptr_op_t args1_node(new expr_t::op_t(expr_t::op_t::O_CONS)); { { expr_t::ptr_op_t arg2_node(new expr_t::op_t(expr_t::op_t::VALUE)); arg2_node->set_value(current->max_width > 0 ? long(current->max_width) : -1); args1_node->set_left(arg2_node); } { expr_t::ptr_op_t arg3_node(new expr_t::op_t(expr_t::op_t::VALUE)); arg3_node->set_value(! current->has_flags(ELEMENT_ALIGN_LEFT)); args1_node->set_right(arg3_node); } } args2_node->set_right(args1_node); } args3_node->set_right(args2_node); } } call2_node->set_right(args3_node); } } current->min_width = 0; current->max_width = 0; string prev_expr = boost::get<expr_t>(current->data).text(); expr_t::ptr_op_t colorize_op; if (op->kind == expr_t::op_t::O_CONS) colorize_op = op->has_right() ? op->right() : NULL; if (colorize_op) { expr_t::ptr_op_t call3_node(new expr_t::op_t(expr_t::op_t::O_CALL)); { call3_node->set_left(ident_node("ansify_if")); { expr_t::ptr_op_t args4_node(new expr_t::op_t(expr_t::op_t::O_CONS)); { args4_node->set_left(call2_node); // from above args4_node->set_right(colorize_op); } call3_node->set_right(args4_node); } } current->data = expr_t(call3_node); } else { current->data = expr_t(call2_node); } boost::get<expr_t>(current->data).set_text(prev_expr); break; } default: throw_(format_error, _f("Unrecognized formatting character: %1%") % *p); } } } if (q != buf) { if (! result.get()) { result.reset(new element_t); current = result.get(); } else { current->next.reset(new element_t); current = current->next.get(); } current->type = element_t::STRING; current->data = string(buf, q); } return result.release(); } string format_t::real_calc(scope_t& scope) { std::ostringstream out_str; for (element_t * elem = elements.get(); elem; elem = elem->next.get()) { std::ostringstream out; string name; if (elem->has_flags(ELEMENT_ALIGN_LEFT)) out << std::left; else out << std::right; switch (elem->type) { case element_t::STRING: if (elem->min_width > 0) out.width(static_cast<std::streamsize>(elem->min_width)); out << boost::get<string>(elem->data); break; case element_t::EXPR: { expr_t& expr(boost::get<expr_t>(elem->data)); try { expr.compile(scope); value_t value; if (expr.is_function()) { call_scope_t args(scope); args.push_back(long(elem->max_width)); value = expr.get_function()(args); } else { value = expr.calc(scope); } DEBUG("format.expr", "value = (" << value << ")"); if (elem->min_width > 0) value.print(out, static_cast<int>(elem->min_width), -1, ! elem->has_flags(ELEMENT_ALIGN_LEFT)); else out << value.to_string(); } catch (const calc_error&) { string current_context = error_context(); add_error_context(_("While calculating format expression:")); add_error_context(expr.context_to_str()); if (! current_context.empty()) add_error_context(current_context); throw; } break; } } if (elem->max_width > 0 || elem->min_width > 0) { unistring temp(out.str()); string result; if (elem->max_width > 0 && elem->max_width < temp.length()) { result = truncate(temp, elem->max_width); } else { result = temp.extract(); if (elem->min_width > temp.length()) for (std::size_t i = 0; i < elem->min_width - temp.length(); i++) result += " "; } out_str << result; } else { out_str << out.str(); } } return out_str.str(); } string format_t::truncate(const unistring& ustr, const std::size_t width, const std::size_t account_abbrev_length) { assert(width < 4095); const std::size_t len = ustr.width(); if (width == 0 || len <= width) return ustr.extract(); std::ostringstream buf; elision_style_t style = default_style; if (account_abbrev_length > 0 && ! default_style_changed) style = ABBREVIATE; switch (style) { case TRUNCATE_LEADING: // This method truncates at the beginning. buf << ".." << ustr.extract_by_width(len - (width - 2), width - 2); break; case TRUNCATE_MIDDLE: // This method truncates in the middle. buf << ustr.extract_by_width(0, (width - 2) / 2) << ".." << ustr.extract_by_width(len - ((width - 2) / 2 + (width - 2) % 2), (width - 2) / 2 + (width - 2) % 2); break; case ABBREVIATE: if (account_abbrev_length > 0) { // The algorithm here is complex, but aims to preserve the most // information in the most useful places. // // Consider: You have an account name like // 'Assets:Banking:Check:Register'. This account name, which is // 29 characters long, must be shortened to fit in 20. How would // you shorten it? // // The approach taken below is to compute the difference, or 9 // characters, and then distribute this difference semi-evenly // among first three segments of the account name, by taking // characters until the difference is gone. Further, earlier // segments will give up more of their share of letters than later // segments, since the later segments usually contain more useful // information. // First, chop up the Unicode string into individual segments. std::list<string> parts; string::size_type beg = 0; string strcopy(ustr.extract()); for (string::size_type pos = strcopy.find(':'); pos != string::npos; beg = pos + 1, pos = strcopy.find(':', beg)) parts.push_back(string(strcopy, beg, pos - beg)); parts.push_back(string(strcopy, beg)); DEBUG("format.abbrev", "Account name: " << strcopy); DEBUG("format.abbrev", "Must fit a " << len << " char string in " << width << " chars"); // Figure out the lengths of all the parts. The last part is // always displayed in full, while the former parts are // distributed, with the latter parts being longer than the // former, but with none shorter than account_abbrev_length. std::list<std::size_t> lens; #if DEBUG_ON int index = 0; #endif for (std::list<string>::iterator i = parts.begin(); i != parts.end(); i++) { std::size_t l = unistring(*i).width(); DEBUG("format.abbrev", "Segment " << ++index << " is " << l << " chars wide"); lens.push_back(l); } // Determine the "overflow", or how many chars in excess we are. std::size_t overflow = len - width; DEBUG("format.abbrev", "There are " << overflow << " chars of overflow"); // Walk through the first n-1 segments, and start subtracting // letters to decrease the overflow. This is done in multiple // passes until the overflow is gone, or we cannot reduce any // further. The calculation to find the amount to remove is: // // overflow * (((len(segment) + counter) * iteration) / // (len(string) - len(last_segment) - counter)) // // Where: // overflow - the amount that needs to be removed // counter - starts at n-1 for the first segment, then // decreases by one until it reaches 0 for the // last segment (which is never shortened). // This value is used to weight the shrinkage // so that earlier segments shrink faster. // iteration - starts at 1, increase by 1 for every // iteration of the loop // // In the example above, we have this account name: // // Assets:Banking:Check:Register // // Therefore, the amount to be removed from Assets is calculated as: // // 9 * (((6 + 3) * 1) / (29 - 8 - 3)) = ceil(4.5) = 5 // // However, since removing 5 chars would make the length of the // segment shorter than the default minimum of 2, we can only // remove 4 chars from Assets to reduce the overflow. And on it // goes. // // The final result will be: As:Ban:Chec:Register std::size_t iteration = 1; std::size_t len_minus_last = len - lens.back(); while (overflow > 0) { std::size_t overflow_at_start = overflow; DEBUG("format.abbrev", "Overflow starting at " << overflow << " chars"); #if DEBUG_ON index = 0; #endif std::size_t counter = lens.size(); std::list<string>::iterator x = parts.begin(); for (std::list<std::size_t>::iterator i = lens.begin(); i != lens.end(); i++) { if (--counter == 0 || overflow == 0) break; DEBUG("format.abbrev", "Overflow is " << overflow << " chars"); std::size_t adjust; if (overflow == 1) adjust = 1; else adjust = std::size_t (std::ceil(double(overflow) * ((double(*i + counter*3) * double(iteration)) / (double(len_minus_last) - double(counter))))); DEBUG("format.abbrev", "Weight calc: (" << overflow << " * (((" << *i << " + " << counter << ") * " << iteration << ") / (" << len_minus_last << " - " << counter << ")))"); if (adjust == 0) adjust = 1; else if (adjust > overflow) adjust = overflow; DEBUG("format.abbrev", "The weighted part is " << adjust << " chars"); std::size_t slack = *i - std::min(*i, account_abbrev_length); if (adjust > slack) adjust = slack; if (adjust > 0) { DEBUG("format.abbrev", "Reducing segment " << ++index << " by " << adjust << " chars"); while (std::isspace((*x)[*i - adjust - 1]) && adjust < *i) { DEBUG("format.abbrev", "Segment ends in whitespace, adjusting down"); ++adjust; } (*i) -= adjust; DEBUG("format.abbrev", "Segment " << index << " is now " << *i << " chars wide"); if (adjust > overflow) overflow = 0; else overflow -= adjust; DEBUG("format.abbrev", "Overflow is now " << overflow << " chars"); } ++x; } DEBUG("format.abbrev", "Overflow ending this time at " << overflow << " chars"); if (overflow == overflow_at_start) break; iteration++; } assert(parts.size() == lens.size()); std::list<string>::iterator i = parts.begin(); std::list<std::size_t>::iterator l = lens.begin(); std::ostringstream result; for (; i != parts.end() && l != lens.end(); i++, l++) { std::list<string>::iterator x = i; if (++x == parts.end()) { result << *i; break; } unistring temp(*i); if (temp.width() > *l) result << temp.extract_by_width(0, *l) << ":"; else result << *i << ":"; } if (overflow > 0) { // Even abbreviated its too big to show the last account, so // abbreviate all but the last and truncate at the beginning. unistring temp(result.str()); assert(temp.width() > width - 2); buf << ".." << temp.extract_by_width(temp.width() - (width - 2), width - 2); } else { buf << result.str(); } break; } // fall through... case TRUNCATE_TRAILING: // This method truncates at the end (the default). buf << ustr.extract_by_width(0, width - 2) << ".."; break; } return buf.str(); } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/format.h���������������������������������������������������������������������������0000664�0000000�0000000�00000011314�14411236400�0015204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file format.h * @author John Wiegley * * @ingroup expr */ #ifndef INCLUDED_FORMAT_H #define INCLUDED_FORMAT_H #include "expr.h" #include "unistring.h" #include <boost/smart_ptr/scoped_ptr.hpp> namespace ledger { class unistring; DECLARE_EXCEPTION(format_error, std::runtime_error); class format_t : public expr_base_t<string>, public noncopyable { typedef expr_base_t<string> base_type; struct element_t : public supports_flags<>, public noncopyable { #define ELEMENT_ALIGN_LEFT 0x01 enum kind_t { STRING, EXPR }; kind_t type; std::size_t min_width; std::size_t max_width; variant<string, expr_t> data; scoped_ptr<struct element_t> next; element_t() throw() : supports_flags<>(), type(STRING), min_width(0), max_width(0) { TRACE_CTOR(element_t, ""); } ~element_t() throw() { TRACE_DTOR(element_t); } element_t& operator=(const element_t& elem) { if (this != &elem) { supports_flags<>::operator=(elem); type = elem.type; min_width = elem.min_width; max_width = elem.max_width; data = elem.data; } return *this; } friend inline void mark_red(std::ostream& out, const element_t * elem) { out.setf(std::ios::left); out.width(0); out << "\033[31m"; if (elem->has_flags(ELEMENT_ALIGN_LEFT)) out << std::left; else out << std::right; if (elem->min_width > 0) out.width(static_cast<std::streamsize>(elem->min_width)); } void dump(std::ostream& out) const; }; scoped_ptr<element_t> elements; public: static enum elision_style_t { TRUNCATE_TRAILING, TRUNCATE_MIDDLE, TRUNCATE_LEADING, ABBREVIATE } default_style; static bool default_style_changed; private: static element_t * parse_elements(const string& fmt, const optional<format_t&>& tmpl); public: format_t() : base_type() { TRACE_CTOR(format_t, ""); } format_t(const string& _str, scope_t * context = NULL) : base_type(context) { if (! _str.empty()) parse_format(_str); TRACE_CTOR(format_t, "const string&"); } virtual ~format_t() { TRACE_DTOR(format_t); } void parse_format(const string& _format, const optional<format_t&>& tmpl = none) { elements.reset(parse_elements(_format, tmpl)); set_text(_format); } virtual void mark_uncompiled() { for (element_t * elem = elements.get(); elem; elem = elem->next.get()) { if (elem->type == element_t::EXPR) { expr_t& expr(boost::get<expr_t>(elem->data)); expr.mark_uncompiled(); } } } virtual result_type real_calc(scope_t& scope); virtual void dump(std::ostream& out) const { for (const element_t * elem = elements.get(); elem; elem = elem->next.get()) elem->dump(out); } static string truncate(const unistring& str, const std::size_t width, const std::size_t account_abbrev_length = 0); }; } // namespace ledger #endif // INCLUDED_FORMAT_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/generate.cc������������������������������������������������������������������������0000664�0000000�0000000�00000024167�14411236400�0015656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "generate.h" #include "session.h" namespace ledger { generate_posts_iterator::generate_posts_iterator (session_t& _session, unsigned int _seed, std::size_t _quantity) : session(_session), seed(_seed), quantity(_quantity), rnd_gen(seed == 0 ? static_cast<unsigned int>(std::time(0)) : seed), year_range(1900, 2300), year_gen(rnd_gen, year_range), mon_range(1, 12), mon_gen(rnd_gen, mon_range), day_range(1, 28), day_gen(rnd_gen, day_range), upchar_range('A', 'Z'), upchar_gen(rnd_gen, upchar_range), downchar_range('a', 'z'), downchar_gen(rnd_gen, downchar_range), numchar_range('0', '9'), numchar_gen(rnd_gen, numchar_range), truth_range(0, 1), truth_gen(rnd_gen, truth_range), three_range(1, 3), three_gen(rnd_gen, three_range), six_range(1, 6), six_gen(rnd_gen, six_range), two_six_range(2, 6), two_six_gen(rnd_gen, two_six_range), strlen_range(1, 40), strlen_gen(rnd_gen, strlen_range), neg_number_range(-10000, -1), neg_number_gen(rnd_gen, neg_number_range), pos_number_range(1, 10000), pos_number_gen(rnd_gen, pos_number_range) { std::ostringstream next_date_buf; generate_date(next_date_buf); next_date = parse_date(next_date_buf.str()); std::ostringstream next_aux_date_buf; generate_date(next_aux_date_buf); next_aux_date = parse_date(next_aux_date_buf.str()); TRACE_CTOR(generate_posts_iterator, "bool"); } void generate_posts_iterator::generate_string(std::ostream& out, int len, bool only_alpha) { DEBUG("generate.post.string", "Generating string of length " << len << ", only alpha " << only_alpha); int last = -1; bool first = true; for (int i = 0; i < len; i++) { int next = only_alpha ? 3 : three_gen(); bool output = true; switch (next) { case 1: // colon if (! first && last == 3 && strlen_gen() % 10 == 0 && i + 1 != len) out << ':'; else { i--; output = false; } break; case 2: // space if (! first && last == 3 && strlen_gen() % 20 == 0 && i + 1 != len) out << ' '; else { i--; output = false; } break; case 3: // character switch (three_gen()) { case 1: // uppercase out << char(upchar_gen()); break; case 2: // lowercase out << char(downchar_gen()); break; case 3: // number if (! only_alpha && ! first) out << char(numchar_gen()); else { i--; output = false; } break; } break; } if (output) { last = next; first = false; } } } bool generate_posts_iterator::generate_account(std::ostream& out, bool no_virtual) { bool must_balance = true; bool is_virtual = false; if (! no_virtual) { switch (three_gen()) { case 1: out << '['; is_virtual = true; break; case 2: out << '('; must_balance = false; is_virtual = true; break; case 3: break; } } generate_string(out, strlen_gen()); if (is_virtual) { if (must_balance) out << ']'; else out << ')'; } return must_balance; } void generate_posts_iterator::generate_commodity(std::ostream& out, const string& exclude) { string comm; do { std::ostringstream buf; generate_string(buf, six_gen(), true); comm = buf.str(); } while (comm == exclude || comm == "h" || comm == "m" || comm == "s" || comm == "and" || comm == "any" || comm == "all" || comm == "div" || comm == "false" || comm == "or" || comm == "not" || comm == "true" || comm == "if" || comm == "else"); out << comm; } string generate_posts_iterator::generate_amount(std::ostream& out, value_t not_this_amount, bool no_negative, const string& exclude) { std::ostringstream buf; if (truth_gen()) { // commodity goes in front generate_commodity(buf, exclude); if (truth_gen()) buf << ' '; if (no_negative || truth_gen()) buf << pos_number_gen(); else buf << neg_number_gen(); } else { if (no_negative || truth_gen()) buf << pos_number_gen(); else buf << neg_number_gen(); if (truth_gen()) buf << ' '; generate_commodity(buf, exclude); } // Possibly generate an annotized commodity, but make it rarer if (! no_negative && three_gen() == 1) { if (three_gen() == 1) { buf << " {"; generate_amount(buf, value_t(), true); buf << '}'; } if (six_gen() == 1) { buf << " ["; generate_date(buf); buf << ']'; } if (six_gen() == 1) { buf << " ("; generate_string(buf, six_gen()); buf << ')'; } } if (! not_this_amount.is_null() && value_t(buf.str()).as_amount().commodity() == not_this_amount.as_amount().commodity()) return ""; out << buf.str(); return buf.str(); } bool generate_posts_iterator::generate_post(std::ostream& out, bool no_amount) { out << " "; bool must_balance = generate_account(out, no_amount); out << " "; if (! no_amount) { value_t amount(generate_amount(out)); if (truth_gen()) generate_cost(out, amount); } if (truth_gen()) generate_note(out); out << '\n'; return must_balance; } void generate_posts_iterator::generate_cost(std::ostream& out, value_t amount) { std::ostringstream buf; if (truth_gen()) buf << " @ "; else buf << " @@ "; if (! generate_amount(buf, amount, true, amount.as_amount().commodity().symbol()).empty()) out << buf.str(); } void generate_posts_iterator::generate_date(std::ostream& out) { out.width(4); out.fill('0'); out << year_gen(); out.width(1); out << '/'; out.width(2); out.fill('0'); out << mon_gen(); out.width(1); out << '/'; out.width(2); out.fill('0'); out << day_gen(); } void generate_posts_iterator::generate_state(std::ostream& out) { switch (three_gen()) { case 1: out << "* "; break; case 2: out << "! "; break; case 3: out << ""; break; } } void generate_posts_iterator::generate_code(std::ostream& out) { out << '('; generate_string(out, six_gen()); out << ") "; } void generate_posts_iterator::generate_payee(std::ostream& out) { generate_string(out, strlen_gen()); } void generate_posts_iterator::generate_note(std::ostream& out) { out << "\n ; "; generate_string(out, strlen_gen()); } void generate_posts_iterator::generate_xact(std::ostream& out) { out << format_date(next_date, FMT_WRITTEN); next_date += gregorian::days(six_gen()); if (truth_gen()) { out << '='; out << format_date(next_aux_date, FMT_WRITTEN); next_aux_date += gregorian::days(six_gen()); } out << ' '; generate_state(out); generate_code(out); generate_payee(out); if (truth_gen()) generate_note(out); out << '\n'; int count = three_gen() * 2; bool has_must_balance = false; for (int i = 0; i < count; i++) { if (generate_post(out)) has_must_balance = true; } if (has_must_balance) generate_post(out, true); out << '\n'; } void generate_posts_iterator::increment() { post_t * post = *posts++; if (post == NULL && quantity > 0) { std::ostringstream buf; generate_xact(buf); DEBUG("generate.post", "The post we intend to parse:\n" << buf.str()); try { shared_ptr<std::istringstream> in(new std::istringstream(buf.str())); parse_context_stack_t parsing_context; parsing_context.push(in); parsing_context.get_current().journal = session.journal.get(); parsing_context.get_current().scope = &session; if (session.journal->read(parsing_context) != 0) { VERIFY(session.journal->xacts.back()->valid()); posts.reset(*session.journal->xacts.back()); post = *posts++; } } catch (std::exception&) { add_error_context(_f("While parsing generated transaction (seed %1%):") % seed); add_error_context(buf.str()); throw; } catch (int) { add_error_context(_f("While parsing generated transaction (seed %1%):") % seed); add_error_context(buf.str()); throw; } quantity--; } m_node = post; } } // namespace ledger ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/generate.h�������������������������������������������������������������������������0000664�0000000�0000000�00000010342�14411236400�0015506�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup generate */ /** * @file generate.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_GENERATE_H #define INCLUDED_GENERATE_H #include "iterators.h" namespace ledger { class session_t; class generate_posts_iterator : public iterator_facade_base<generate_posts_iterator, post_t *, boost::forward_traversal_tag> { session_t& session; unsigned int seed; std::size_t quantity; date_t next_date; date_t next_aux_date; mt19937 rnd_gen; typedef variate_generator<mt19937&, uniform_int<> > int_generator_t; typedef variate_generator<mt19937&, uniform_real<> > real_generator_t; uniform_int<> year_range; int_generator_t year_gen; uniform_int<> mon_range; int_generator_t mon_gen; uniform_int<> day_range; int_generator_t day_gen; uniform_int<> upchar_range; int_generator_t upchar_gen; uniform_int<> downchar_range; int_generator_t downchar_gen; uniform_int<> numchar_range; int_generator_t numchar_gen; uniform_int<> truth_range; int_generator_t truth_gen; uniform_int<> three_range; int_generator_t three_gen; uniform_int<> six_range; int_generator_t six_gen; uniform_int<> two_six_range; int_generator_t two_six_gen; uniform_int<> strlen_range; int_generator_t strlen_gen; uniform_real<> neg_number_range; real_generator_t neg_number_gen; uniform_real<> pos_number_range; real_generator_t pos_number_gen; xact_posts_iterator posts; public: generate_posts_iterator(session_t& _session, unsigned int _seed = 0, std::size_t _quantity = 100); virtual ~generate_posts_iterator() throw() { TRACE_DTOR(generate_posts_iterator); } virtual void increment(); protected: void generate_string(std::ostream& out, int len, bool only_alpha = false); bool generate_account(std::ostream& out, bool no_virtual = false); void generate_commodity(std::ostream& out, const string& exclude = ""); string generate_amount(std::ostream& out, value_t not_this_amount = NULL_VALUE, bool no_negative = false, const string& exclude = ""); bool generate_post(std::ostream& out, bool no_amount = false); void generate_cost(std::ostream& out, value_t amount); void generate_date(std::ostream& out); void generate_state(std::ostream& out); void generate_code(std::ostream& out); void generate_payee(std::ostream& out); void generate_note(std::ostream& out); void generate_xact(std::ostream& out); }; } // namespace ledger #endif // INCLUDED_GENERATE_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/global.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000037327�14411236400�0015326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "global.h" #if HAVE_BOOST_PYTHON #include "pyinterp.h" #else #include "session.h" #endif #include "item.h" #include "journal.h" #include "pool.h" namespace ledger { static bool args_only = false; std::string _init_file; global_scope_t::global_scope_t(char ** envp) { epoch = CURRENT_TIME(); #if HAVE_BOOST_PYTHON if (! python_session.get()) { python_session.reset(new ledger::python_interpreter_t); session_ptr = python_session; } #else session_ptr.reset(new session_t); #endif set_session_context(session_ptr.get()); // Create the report object, which maintains state relating to each // command invocation. Because we're running from main(), the // distinction between session and report doesn't really matter, but if // a GUI were calling into Ledger it would have one session object per // open document, with a separate report_t object for each report it // generated. report_stack.push_front(new report_t(*session_ptr)); scope_t::default_scope = &report(); scope_t::empty_scope = &empty_scope; // Read the user's options, in the following order: // // 1. environment variables (LEDGER_<option>) // 2. initialization file (~/.ledgerrc) // 3. command-line (--option or -o) // // Before processing command-line options, we must notify the session object // that such options are beginning, since options like -f cause a complete // override of files found anywhere else. if (! args_only) { session().set_flush_on_next_data_file(true); read_environment_settings(envp); session().set_flush_on_next_data_file(true); read_init(); } else { session().HANDLER(price_db_).off(); } TRACE_CTOR(global_scope_t, ""); } global_scope_t::~global_scope_t() { TRACE_DTOR(global_scope_t); // If memory verification is being performed (which can be very slow), // clean up everything by closing the session and deleting the session // object, and then shutting down the memory tracing subsystem. // Otherwise, let it all leak because we're about to exit anyway. IF_VERIFY() set_session_context(NULL); #if HAVE_BOOST_PYTHON python_session.reset(); #endif } void global_scope_t::parse_init(path init_file) { TRACE_START(init, 1, "Read initialization file"); parse_context_stack_t parsing_context; parsing_context.push(init_file); parsing_context.get_current().journal = session().journal.get(); parsing_context.get_current().scope = &report(); if (session().journal->read(parsing_context) > 0 || session().journal->auto_xacts.size() > 0 || session().journal->period_xacts.size() > 0) { throw_(parse_error, _f("Transactions found in initialization file '%1%'") % init_file); } TRACE_FINISH(init, 1); } void global_scope_t::read_init() { // if specified on the command line init_file_ is filled in // global_scope_t::handle_debug_options. If it was specified on the command line // fail if the file doesn't exist. If no init file was specified // on the command-line then try the default values, but don't fail if there // isn't one. path init_file; if (HANDLED(init_file_)) { init_file=HANDLER(init_file_).str(); if (! exists(init_file)) { throw_(parse_error, _f("Could not find specified init file %1%") % init_file); } } else { // in order, try to read the init file from: // - $XDG_CONFIG_HOME/ledger/ledgerrc // - $HOME/.config/ledger/ledgerrc // - $HOME/.ledgerrc // - ./.ledgerrc if (const char * xdg_config_home = std::getenv("XDG_CONFIG_HOME")) { init_file = (path(xdg_config_home) / "ledger" / "ledgerrc"); } if (! exists(init_file)) { if (const char * home_var = std::getenv("HOME")) { init_file = (path(home_var) / ".config" / "ledger" / "ledgerrc"); if (! exists(init_file)) { init_file = (path(home_var) / ".ledgerrc"); } } if (! exists(init_file)) { init_file = ("./.ledgerrc"); } } } if (exists(init_file)) { parse_init(init_file); } } char * global_scope_t::prompt_string() { static char prompt[32]; std::size_t i; for (i = 0; i < report_stack.size(); i++) prompt[i] = ']'; prompt[i++] = ' '; prompt[i] = '\0'; return prompt; } void global_scope_t::report_error(const std::exception& err) { std::cout.flush(); // first display anything that was pending if (caught_signal == NONE_CAUGHT) { // Display any pending error context information string context = error_context(); if (! context.empty()) std::cerr << context << std::endl; std::cerr << _("Error: ") << err.what() << std::endl; } else { caught_signal = NONE_CAUGHT; } } void global_scope_t::execute_command(strings_list args, bool at_repl) { session().set_flush_on_next_data_file(true); // Process the command verb, arguments and options if (at_repl) { args = read_command_arguments(report(), args); if (args.empty()) return; } strings_list::iterator arg = args.begin(); string verb = *arg++; // Look for a precommand first, which is defined as any defined function // whose name starts with "ledger_precmd_". The difference between a // precommand and a regular command is that precommands ignore the journal // data file completely, nor is the user's init file read. // // Here are some examples of pre-commands: // // parse STRING ; show how a value expression is parsed // eval STRING ; simply evaluate a value expression // format STRING ; show how a format string is parsed // // If such a command is found, create the output stream for the result and // then invoke the command. expr_t::func_t command; bool is_precommand = false; bind_scope_t bound_scope(*this, report()); if (bool(command = look_for_precommand(bound_scope, verb))) is_precommand = true; // If it is not a pre-command, then parse the user's ledger data at this // time if not done already (i.e., if not at a REPL). Then patch up the // report options based on the command verb. if (! is_precommand) { if (! at_repl) session().read_journal_files(); report().normalize_options(verb); if (! bool(command = look_for_command(bound_scope, verb))) throw_(std::logic_error, _f("Unrecognized command '%1%'") % verb); } // Create the output stream (it might be a file, the console or a PAGER // subprocess) and invoke the report command. The output stream is closed // by the caller of this function. report().output_stream .initialize(report().HANDLED(output_) ? optional<path>(path(report().HANDLER(output_).str())) : optional<path>(), report().HANDLED(pager_) ? optional<path>(path(report().HANDLER(pager_).str())) : optional<path>()); // Now that the output stream is initialized, report the options that will // participate in this report, if the user specified --options if (HANDLED(options)) report_options(report(), report().output_stream); // Create an argument scope containing the report command's arguments, and // then invoke the command. The bound scope causes lookups to happen // first in the global scope, and then in the report scope. call_scope_t command_args(bound_scope); for (strings_list::iterator i = arg; i != args.end(); i++) command_args.push_back(string_value(*i)); INFO_START(command, "Finished executing command"); command(command_args); INFO_FINISH(command); } int global_scope_t::execute_command_wrapper(strings_list args, bool at_repl) { int status = 1; try { if (at_repl) push_report(); execute_command(args, at_repl); if (at_repl) pop_report(); // If we've reached this point, everything succeeded fine. Ledger uses // exceptions to notify of error conditions, so if you're using gdb, // just type "catch throw" to find the source point of any error. status = 0; } catch (const std::exception& err) { if (at_repl) pop_report(); report_error(err); } return status; } void global_scope_t::report_options(report_t& report, std::ostream& out) { out << "===============================================================================" << std::endl; out << "[Global scope options]" << std::endl; HANDLER(args_only).report(out); HANDLER(debug_).report(out); HANDLER(init_file_).report(out); HANDLER(script_).report(out); HANDLER(trace_).report(out); HANDLER(verbose).report(out); HANDLER(verify).report(out); HANDLER(verify_memory).report(out); out << std::endl << "[Session scope options]" << std::endl; report.session.report_options(out); out << std::endl << "[Report scope options]" << std::endl; report.report_options(out); out << "===============================================================================" << std::endl; } option_t<global_scope_t> * global_scope_t::lookup_option(const char * p) { switch (*p) { case 'a': OPT(args_only); break; case 'd': OPT(debug_); break; case 'h': OPT_(help); break; case 'i': OPT_(init_file_); break; case 'o': OPT(options); break; case 's': OPT(script_); break; case 't': OPT(trace_); break; case 'v': OPT_(verbose); else OPT(verify); else OPT(verify_memory); else OPT(version); break; } return NULL; } expr_t::ptr_op_t global_scope_t::lookup(const symbol_t::kind_t kind, const string& name) { switch (kind) { case symbol_t::FUNCTION: if (option_t<global_scope_t> * handler = lookup_option(name.c_str())) return MAKE_OPT_FUNCTOR(global_scope_t, handler); break; case symbol_t::OPTION: if (option_t<global_scope_t> * handler = lookup_option(name.c_str())) return MAKE_OPT_HANDLER(global_scope_t, handler); break; case symbol_t::PRECOMMAND: { const char * p = name.c_str(); switch (*p) { case 'p': if (is_eq(p, "push")) return MAKE_FUNCTOR(global_scope_t::push_command); else if (is_eq(p, "pop")) return MAKE_FUNCTOR(global_scope_t::pop_command); break; } } default: break; } // If you're wondering how symbols from report() will be found, it's // because of the bind_scope_t object in execute_command() below. return NULL; } void global_scope_t::read_environment_settings(char * envp[]) { TRACE_START(environment, 1, "Processed environment variables"); process_environment(const_cast<const char **>(envp), "LEDGER_", report()); #if 1 // These are here for backwards compatibility, but are deprecated. if (const char * p = std::getenv("LEDGER")) { if (! std::getenv("LEDGER_FILE")) process_option("environ", "file", report(), p, "LEDGER"); } if (const char * p = std::getenv("LEDGER_INIT")) { if (! std::getenv("LEDGER_INIT_FILE")) process_option("environ", "init-file", report(), p, "LEDGER_INIT"); } if (const char * p = std::getenv("PRICE_HIST")) { if (! std::getenv("LEDGER_PRICE_DB")) process_option("environ", "price-db", report(), p, "PRICE_HIST"); } if (const char * p = std::getenv("PRICE_EXP")) { if (! std::getenv("LEDGER_PRICE_EXP")) process_option("environ", "price-exp", report(), p, "PRICE_EXP"); } #endif TRACE_FINISH(environment, 1); } strings_list global_scope_t::read_command_arguments(scope_t& scope, strings_list args) { TRACE_START(arguments, 1, "Processed command-line arguments"); strings_list remaining = process_arguments(args, scope); TRACE_FINISH(arguments, 1); return remaining; } void global_scope_t::normalize_session_options() { #if LOGGING_ON INFO("Initialization file is " << HANDLER(init_file_).str()); INFO("Price database is " << session().HANDLER(price_db_).str()); foreach (const path& pathname, session().HANDLER(file_).data_files) INFO("Journal file is " << pathname.string()); #endif // LOGGING_ON } expr_t::func_t global_scope_t::look_for_precommand(scope_t& scope, const string& verb) { if (expr_t::ptr_op_t def = scope.lookup(symbol_t::PRECOMMAND, verb)) return def->as_function(); else return expr_t::func_t(); } expr_t::func_t global_scope_t::look_for_command(scope_t& scope, const string& verb) { if (expr_t::ptr_op_t def = scope.lookup(symbol_t::COMMAND, verb)) return def->as_function(); else return expr_t::func_t(); } void global_scope_t::visit_man_page() const { #if !defined(_WIN32) && !defined(__CYGWIN__) int pid = fork(); if (pid < 0) { throw std::logic_error(_("Failed to fork child process")); } else if (pid == 0) { // child execlp("man", "man", "1", "ledger", NULL); // We should never, ever reach here perror("execlp: man"); exit(1); } int status = -1; wait(&status); #endif exit(0); // parent } void handle_debug_options(int argc, char * argv[]) { for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { if (std::strcmp(argv[i], "--args-only") == 0) { args_only = true; } else if (std::strcmp(argv[i], "--verify-memory") == 0) { #if VERIFY_ON verify_enabled = true; _log_level = LOG_DEBUG; _log_category = "memory\\.counts"; #endif } else if (std::strcmp(argv[i], "--verify") == 0) { #if VERIFY_ON verify_enabled = true; #endif } else if (std::strcmp(argv[i], "--verbose") == 0 || std::strcmp(argv[i], "-v") == 0) { #if LOGGING_ON _log_level = LOG_INFO; #endif } else if (i + 1 < argc && std::strcmp(argv[i], "--init-file") == 0) { _init_file = argv[i + 1]; i++; } else if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) { #if DEBUG_ON _log_level = LOG_DEBUG; _log_category = argv[i + 1]; i++; #endif } else if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) { #if TRACING_ON _log_level = LOG_TRACE; try { _trace_level = boost::lexical_cast<uint16_t>(argv[i + 1]); } catch (const boost::bad_lexical_cast&) { throw std::logic_error(_("Argument to --trace must be an integer")); } i++; #endif } } } } } // namespace ledger ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/global.h���������������������������������������������������������������������������0000664�0000000�0000000�00000013536�14411236400�0015164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file global.h * @author John Wiegley * * @brief Contains the top-level functions used by main.cc */ #ifndef INCLUDED_GLOBAL_H #define INCLUDED_GLOBAL_H #include "option.h" #include "report.h" namespace ledger { class session_t; class report_t; extern std::string _init_file; class global_scope_t : public noncopyable, public scope_t { shared_ptr<session_t> session_ptr; ptr_list<report_t> report_stack; empty_scope_t empty_scope; public: global_scope_t(char ** envp); ~global_scope_t(); void quick_close() { if (! report_stack.empty()) report_stack.front().quick_close(); } virtual string description() { return _("global scope"); } void parse_init(path init_file); void read_init(); void read_environment_settings(char * envp[]); strings_list read_command_arguments(scope_t& scope, strings_list args); void normalize_session_options(); expr_t::func_t look_for_precommand(scope_t& scope, const string& verb); expr_t::func_t look_for_command(scope_t& scope, const string& verb); char * prompt_string(); session_t& session() { return *session_ptr.get(); } report_t& report() { return report_stack.front(); } void push_report() { report_stack.push_front(new report_t(report_stack.front())); scope_t::default_scope = &report(); } void pop_report() { assert(! report_stack.empty()); report_stack.pop_front(); // There should always be the "default report" waiting on the stack. assert(! report_stack.empty()); scope_t::default_scope = &report(); } void report_error(const std::exception& err); void execute_command(strings_list args, bool at_repl); /** * @return \c true if a command was actually executed; otherwise, it probably * just resulted in setting some options. */ int execute_command_wrapper(strings_list args, bool at_repl); value_t push_command(call_scope_t&) { // Make a copy at position 2, because the topmost report object has an // open output stream at this point. We want it to get popped off as // soon as this command terminate so that the stream is closed cleanly. report_stack.insert(++report_stack.begin(), new report_t(report_stack.front())); return true; } value_t pop_command(call_scope_t&) { pop_report(); return true; } void show_version_info(std::ostream& out) { out << "Ledger " << Ledger_VERSION_MAJOR << '.' << Ledger_VERSION_MINOR << '.' << Ledger_VERSION_PATCH; if (Ledger_VERSION_PRERELEASE != 0) out << Ledger_VERSION_PRERELEASE; if (Ledger_VERSION_DATE != 0) out << '-' << Ledger_VERSION_DATE; out << _(", the command-line accounting tool"); out << _("\nwith"); #if !HAVE_GPGME out << _("out"); #endif out << _(" support for gpg encrypted journals and with"); #if !HAVE_BOOST_PYTHON out <<_("out"); #endif out << _(" Python support"); out << _("\n\nCopyright (c) 2003-2023, John Wiegley. All rights reserved.\n\n\ This program is made available under the terms of the BSD Public License.\n\ See LICENSE file included with the distribution for details and disclaimer."); out << std::endl; } void report_options(report_t& report, std::ostream& out); option_t<global_scope_t> * lookup_option(const char * p); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); OPTION(global_scope_t, args_only); OPTION(global_scope_t, debug_); void visit_man_page() const; OPTION_(global_scope_t, help, DO() { parent->visit_man_page(); }); // -h OPTION__ (global_scope_t, init_file_, // -i CTOR(global_scope_t, init_file_) { if (!_init_file.empty()) // _init_file is filled during handle_debug_options on(none, _init_file); }); OPTION(global_scope_t, options); OPTION(global_scope_t, script_); OPTION(global_scope_t, trace_); OPTION(global_scope_t, verbose); OPTION(global_scope_t, verify); OPTION(global_scope_t, verify_memory); OPTION_(global_scope_t, version, DO() { // -v parent->show_version_info(std::cout); throw error_count(0, ""); // exit immediately }); }; void handle_debug_options(int argc, char * argv[]); } // namespace ledger #endif // INCLUDED_GLOBAL_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/gpgme.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000015421�14411236400�0015154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2020, Michael Raitza. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "gpgme.h" #include "utils.h" #include <iostream> #include <sstream> #include <cerrno> #include <gpgme++/context.h> #include <gpgme++/decryptionresult.h> namespace ledger { using GpgME::Context; using GpgME::Data; using GpgME::Protocol; using namespace std; using std::shared_ptr; using std::unique_ptr; constexpr static unsigned int _bufsize = 4096; data_streambuffer_t::data_streambuffer_t(Data& _data) : data(_data), bufsize(_bufsize), cbuf(unique_ptr<char[]>(new char[_bufsize])) {} streambuf::int_type data_streambuffer_t::underflow() { if (this->gptr() == this->egptr()) { auto buf = cbuf.get(); auto size = data.read(buf, bufsize); if (size < 0) throw_(runtime_error, _f("Reading decrypted data: GPGME: %1%") % strerror(errno)); else if (size == 0) return char_traits<char>::eof(); this->setg(buf, buf, buf + size); } return this->gptr() == this->egptr() ? char_traits<char>::eof() : char_traits<char>::to_int_type(*this->gptr()); } streambuf::pos_type data_streambuffer_t::seekpos(streambuf::pos_type sp, ios_base::openmode which = ios_base::in) { return this->seekoff(static_cast<streambuf::off_type>(sp), ios::beg, which); } streambuf::pos_type data_streambuffer_t::seekoff(streambuf::off_type off, ios_base::seekdir dir, ios_base::openmode which = ios_base::in) { streamoff pos = -1; if (dir == ios::beg) pos = static_cast<streambuf::pos_type>(data.seek(static_cast<streamoff>(off), SEEK_SET)); else if (dir == ios::end) pos = static_cast<streambuf::pos_type>(data.seek(static_cast<streamoff>(off), SEEK_END)); else if (dir == ios::cur) pos = static_cast<streambuf::pos_type>(data.seek(static_cast<streamoff>(off), SEEK_CUR)); if (pos == -1) throw runtime_error("Unable to seek to position"); auto buf = cbuf.get(); // Trigger underflow on next read this->setg(buf, buf+1, buf+1); return pos; } FILE * decrypted_stream_t::open_file(const path& filename) { FILE * f = fopen(filename.c_str(), "rb"); if (!f) throw_(runtime_error, _f("Could not open file: %1%") % strerror(errno)); return f; } shared_ptr<Data> decrypted_stream_t::setup_cipher_buffer(FILE * f) { auto enc_d = make_shared<Data>(f); if (!enc_d) throw runtime_error("Unable to create cipher text buffer"); if (enc_d->type() != Data::PGPEncrypted && enc_d->type() != Data::CMSEncrypted && enc_d->type() != Data::Unknown && enc_d->type() != Data::Invalid) throw_(runtime_error, _f("Unsupported encryption type: %1%") % enc_d->type()); return enc_d; } static bool is_encrypted(shared_ptr<Data> enc_d) { if (enc_d->type() == Data::Unknown || enc_d->type() == Data::Invalid) return false; else return true; } shared_ptr<Data> decrypted_stream_t::decrypt(shared_ptr<Data> enc_d) { unique_ptr<Context> ctx; shared_ptr<Data> dec_d; if (enc_d->type() == Data::Unknown || enc_d->type() == Data::Invalid) { ctx = nullptr; dec_d = enc_d; } else { #if GPGME_VERSION_NUMBER < 0x010d00 ctx = unique_ptr<Context>(Context::createForProtocol(enc_d->type() == Data::PGPEncrypted ? Protocol::OpenPGP : Protocol::CMS)); #else ctx = Context::create(enc_d->type() == Data::PGPEncrypted ? Protocol::OpenPGP : Protocol::CMS); #endif if (!ctx) throw runtime_error("Unable to establish decryption context"); ctx->setOffline(true); dec_d = make_shared<Data>(); if (!dec_d) throw runtime_error("Unable to create plain text buffer"); auto res = ctx->decrypt(*enc_d.get(), *dec_d.get()); if (res.error()) throw_(runtime_error, _f("Decryption error: %1%: %2%") % res.error().source() % res.error().asString()); } return dec_d; } static inline void init_lib() { auto err = GpgME::initializeLibrary(0); if (err.code() != GPG_ERR_NO_ERROR) throw_(runtime_error, _f("%1%: %2%") % err.source() % err.asString()); } static inline void rewind(Data * d) { #if GPGME_VERSION_NUMBER < 0x010c00 d->seek(0, SEEK_SET); #else d->rewind(); #endif } istream* decrypted_stream_t::open_stream(const path& filename) { init_lib(); unique_ptr<FILE, decltype(&fclose)> file(open_file(filename), &fclose); auto enc_d = setup_cipher_buffer(file.get()); if (is_encrypted(enc_d)) { auto dec_d = decrypt(enc_d); rewind(dec_d.get()); return new decrypted_stream_t(dec_d); } return new ifstream(filename); } decrypted_stream_t::decrypted_stream_t(path& filename) : istream(new data_streambuffer_t(*new Data())) { init_lib(); file = open_file(filename); auto enc_d = setup_cipher_buffer(file); dec_d = decrypt(enc_d); rewind(dec_d.get()); if (is_encrypted(enc_d)) { fclose(file); file = nullptr; } set_rdbuf(new data_streambuffer_t(*dec_d.get())); clear(); } decrypted_stream_t::decrypted_stream_t(shared_ptr<Data> dec_d) : istream(new data_streambuffer_t(*dec_d.get())), dec_d(dec_d), file(nullptr) { clear(); } decrypted_stream_t::~decrypted_stream_t() { if (file) fclose(file); } } // namespace ledger �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/gpgme.h����������������������������������������������������������������������������0000664�0000000�0000000�00000011530�14411236400�0015013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2020, Michael Raitza. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include <system.hh> #include "utils.h" #include <streambuf> #include <istream> #include <gpgme++/data.h> namespace ledger { class data_streambuffer_t : public std::streambuf { public: GpgME::Data& data; /* Size of cbuf */ const unsigned int bufsize; /* Backing character buffer */ std::unique_ptr<char[]> cbuf; explicit data_streambuffer_t(GpgME::Data& _data); virtual int_type underflow(); protected: virtual std::streambuf::pos_type seekpos(std::streambuf::pos_type sp, std::ios_base::openmode which); virtual std::streambuf::pos_type seekoff(std::streambuf::off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which); }; class decrypted_stream_t : public std::istream { public: std::shared_ptr<GpgME::Data> dec_d; std::FILE * file; /* Establishes an istream decrypting a file pointed to by FILENAME. Decryption is performed at object creation and only the decrypted Data buffer is retained as the backing store for the stream. Expects the input file to be unencrypted or encrypted in CMS or PGP format (includes asymmetrically and symmetrically encrypted content). Calls open_file(), setup_cipher_buffer() and decrypt() and throws exceptions noted in there on error. */ decrypted_stream_t(path& filename); /* Established an istream serving the decrypted content in DEC_D. Make sure DEC_D is properly rewound. (Which it is not after decrypting.) Expects DEC_D was created by actually decrypting input data (usually a FILE object). Otherwise GpgME just hands over the reference from the buffer holding the "encrypted" input to DEC_D. Then, you must keep the original object around for the lifetime of this stream. */ decrypted_stream_t(std::shared_ptr<GpgME::Data> dec_d); ~decrypted_stream_t(); /* Opens file pointed to by FILENAME. Opens the file using fopen() in "rb" mode. Throws a runtime error when the file cannot be opened for reading. */ static std::FILE * open_file(const path& filename); /* Returns a Data buffer connected to an open FILE object. Throws a runtime error when the content is neither PGPEncrypted, CMSEncrypted or Unknown, or when the buffer cannot be established. */ static std::shared_ptr<GpgME::Data> setup_cipher_buffer(std::FILE * f); /* Returns a Data buffer of the plain text. Decrypts cipher text by establishing a proper decryption context, first. . Returns the input Data buffer when the encryption type is Unknown, which is considered unencrypted input. Throws a runtime error when the decryption fails or when the cipher text is neither PGPEncrypted nor CMSEncrypted. */ static std::shared_ptr<GpgME::Data> decrypt(std::shared_ptr<GpgME::Data> enc_d); /* Returns an istream, which is either a decrypted_stream_t, given the file is encrypted, or an ifstream object. Use this to create the istream! The decrypted_stream_t is perfectly capable reading unencrypted data, but the file size and data pointers no longer match with a standard ifstream. */ static std::istream* open_stream(const path& filename); }; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/history.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000047226�14411236400�0015566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/filtered_graph.hpp> #include <boost/graph/dijkstra_shortest_paths.hpp> #include <boost/graph/graphviz.hpp> #include "history.h" template <typename T> struct f_max : public std::binary_function<T, T, bool> { T operator()(const T& x, const T& y) const { return std::max(x, y); } }; namespace boost { enum edge_price_point_t { edge_price_point }; enum edge_price_ratio_t { edge_price_ratio }; BOOST_INSTALL_PROPERTY(edge, price_point); BOOST_INSTALL_PROPERTY(edge, price_ratio); } namespace ledger { class commodity_history_impl_t : public noncopyable { public: typedef adjacency_list <vecS, // Store all edges in a vector vecS, // Store all vertices in a vector undirectedS, // Relations are both ways // All vertices are commodities property<vertex_name_t, const commodity_t *, property<vertex_index_t, std::size_t> >, // All edges are weights computed as the absolute difference between // the reference time of a search and a known price point. A // filtered_graph is used to select the recent price point to the // reference time before performing the search. property<edge_weight_t, long, property<edge_price_ratio_t, price_map_t, property<edge_price_point_t, price_point_t> > >, // Graph itself has an std::string name property<graph_name_t, std::string> > Graph; Graph price_graph; typedef graph_traits<Graph>::vertex_descriptor vertex_descriptor; typedef graph_traits<Graph>::edge_descriptor edge_descriptor; typedef property_map<Graph, vertex_name_t>::type NameMap; typedef property_map<Graph, edge_weight_t>::type EdgeWeightMap; typedef property_map<Graph, edge_price_point_t>::type PricePointMap; typedef property_map<Graph, edge_price_ratio_t>::type PriceRatioMap; PricePointMap pricemap; PriceRatioMap ratiomap; commodity_history_impl_t() : pricemap(get(edge_price_point, price_graph)), ratiomap(get(edge_price_ratio, price_graph)) {} void add_commodity(commodity_t& comm); void add_price(const commodity_t& source, const datetime_t& when, const amount_t& price); void remove_price(const commodity_t& source, const commodity_t& target, const datetime_t& date); void map_prices(function<void(datetime_t, const amount_t&)> fn, const commodity_t& source, const datetime_t& moment, const datetime_t& _oldest = datetime_t(), bool bidirectionally = false); optional<price_point_t> find_price(const commodity_t& source, const datetime_t& moment, const datetime_t& oldest = datetime_t()); optional<price_point_t> find_price(const commodity_t& source, const commodity_t& target, const datetime_t& moment, const datetime_t& oldest = datetime_t()); void print_map(std::ostream& out, const datetime_t& moment = datetime_t()); }; commodity_history_t::commodity_history_t() { p_impl.reset(new commodity_history_impl_t); } commodity_history_t::~commodity_history_t() { } void commodity_history_t::add_commodity(commodity_t& comm) { p_impl->add_commodity(comm); } void commodity_history_t::add_price(const commodity_t& source, const datetime_t& when, const amount_t& price) { p_impl->add_price(source, when, price); } void commodity_history_t::remove_price(const commodity_t& source, const commodity_t& target, const datetime_t& date) { p_impl->remove_price(source, target, date); } void commodity_history_t::map_prices( function<void(datetime_t, const amount_t&)> fn, const commodity_t& source, const datetime_t& moment, const datetime_t& _oldest, bool bidirectionally) { p_impl->map_prices(fn, source, moment, _oldest, bidirectionally); } optional<price_point_t> commodity_history_t::find_price(const commodity_t& source, const datetime_t& moment, const datetime_t& oldest) { return p_impl->find_price(source, moment, oldest); } optional<price_point_t> commodity_history_t::find_price(const commodity_t& source, const commodity_t& target, const datetime_t& moment, const datetime_t& oldest) { return p_impl->find_price(source, target, moment, oldest); } void commodity_history_t::print_map(std::ostream& out, const datetime_t& moment) { p_impl->print_map(out, moment); } template <typename EdgeWeightMap, typename PricePointMap, typename PriceRatioMap> class recent_edge_weight { public: EdgeWeightMap weight; PricePointMap price_point; PriceRatioMap ratios; datetime_t reftime; datetime_t oldest; recent_edge_weight() { } recent_edge_weight(EdgeWeightMap _weight, PricePointMap _price_point, PriceRatioMap _ratios, const datetime_t& _reftime, const datetime_t& _oldest = datetime_t()) : weight(_weight), price_point(_price_point), ratios(_ratios), reftime(_reftime), oldest(_oldest) { } template <typename Edge> bool operator()(const Edge& e) const { #if DEBUG_ON DEBUG("history.find", " reftime = " << reftime); if (! oldest.is_not_a_date_time()) { DEBUG("history.find", " oldest = " << oldest); } #endif const price_map_t& prices(get(ratios, e)); if (prices.empty()) { DEBUG("history.find", " prices map is empty for this edge"); return false; } price_map_t::const_iterator low = prices.upper_bound(reftime); if (low != prices.end() && low == prices.begin()) { DEBUG("history.find", " don't use this edge"); return false; } else { --low; assert(((*low).first <= reftime)); if (! oldest.is_not_a_date_time() && (*low).first < oldest) { DEBUG("history.find", " edge is out of range"); return false; } long secs = (reftime - (*low).first).total_seconds(); assert(secs >= 0); put(weight, e, secs); put(price_point, e, price_point_t((*low).first, (*low).second)); DEBUG("history.find", " using edge at price point " << (*low).first << " " << (*low).second); return true; } } }; typedef filtered_graph <commodity_history_impl_t::Graph, recent_edge_weight<commodity_history_impl_t::EdgeWeightMap, commodity_history_impl_t::PricePointMap, commodity_history_impl_t::PriceRatioMap> > FGraph; typedef property_map<FGraph, vertex_name_t>::type FNameMap; typedef property_map<FGraph, vertex_index_t>::type FIndexMap; typedef iterator_property_map <commodity_history_impl_t::vertex_descriptor*, FIndexMap, commodity_history_impl_t::vertex_descriptor, commodity_history_impl_t::vertex_descriptor&> FPredecessorMap; typedef iterator_property_map<long*, FIndexMap, long, long&> FDistanceMap; void commodity_history_impl_t::add_commodity(commodity_t& comm) { if (! comm.graph_index()) { comm.set_graph_index(num_vertices(price_graph)); add_vertex(/* vertex_name= */ &comm, price_graph); } } void commodity_history_impl_t::add_price(const commodity_t& source, const datetime_t& when, const amount_t& price) { assert(source != price.commodity()); vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*price.commodity().graph_index(), price_graph); std::pair<edge_descriptor, bool> e1 = edge(sv, tv, price_graph); if (! e1.second) e1 = add_edge(sv, tv, price_graph); price_map_t& prices(get(ratiomap, e1.first)); std::pair<price_map_t::iterator, bool> result = prices.insert(price_map_t::value_type(when, price)); if (! result.second) { // There is already an entry for this moment, so update it (*result.first).second = price; } } void commodity_history_impl_t::remove_price(const commodity_t& source, const commodity_t& target, const datetime_t& date) { assert(source != target); vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); std::pair<Graph::edge_descriptor, bool> e1 = edge(sv, tv, price_graph); if (e1.second) { price_map_t& prices(get(ratiomap, e1.first)); // jww (2012-03-04): If it fails, should we give a warning? prices.erase(date); if (prices.empty()) remove_edge(e1.first, price_graph); } } void commodity_history_impl_t::map_prices( function<void(datetime_t, const amount_t&)> fn, const commodity_t& source, const datetime_t& moment, const datetime_t& oldest, bool bidirectionally) { DEBUG("history.map", "Mapping prices for source commodity: " << source); vertex_descriptor sv = vertex(*source.graph_index(), price_graph); FGraph fg(price_graph, recent_edge_weight<EdgeWeightMap, PricePointMap, PriceRatioMap> (get(edge_weight, price_graph), pricemap, ratiomap, moment, oldest)); FNameMap namemap(get(vertex_name, fg)); graph_traits<FGraph>::adjacency_iterator f_vi, f_vend; for (boost::tuples::tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { std::pair<Graph::edge_descriptor, bool> edgePair = edge(sv, *f_vi, fg); Graph::edge_descriptor edge = edgePair.first; const price_map_t& prices(get(ratiomap, edge)); foreach (const price_map_t::value_type& pair, prices) { const datetime_t& when(pair.first); DEBUG("history.map", "Price " << pair.second << " on " << when); if ((oldest.is_not_a_date_time() || when >= oldest) && when <= moment) { if (pair.second.commodity() == source) { if (bidirectionally) { amount_t price(pair.second); price.in_place_invert(); if (source == *get(namemap, sv)) price.set_commodity(const_cast<commodity_t&>(*get(namemap, *f_vi))); else price.set_commodity(const_cast<commodity_t&>(*get(namemap, sv))); DEBUG("history.map", "Inverted price is " << price); DEBUG("history.map", "fn(" << when << ", " << price << ")"); fn(when, price); } } else { DEBUG("history.map", "fn(" << when << ", " << pair.second << ")"); fn(when, pair.second); } } } } } optional<price_point_t> commodity_history_impl_t::find_price(const commodity_t& source, const datetime_t& moment, const datetime_t& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); FGraph fg(price_graph, recent_edge_weight<EdgeWeightMap, PricePointMap, PriceRatioMap> (get(edge_weight, price_graph), pricemap, ratiomap, moment, oldest)); FNameMap namemap(get(vertex_name, fg)); DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); #if DEBUG_ON if (source.has_flags(COMMODITY_PRIMARY)) DEBUG("history.find", "sv commodity is primary"); #endif DEBUG("history.find", "tv commodity = none "); datetime_t most_recent = moment; amount_t price; graph_traits<FGraph>::adjacency_iterator f_vi, f_vend; for (boost::tuples::tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { std::pair<Graph::edge_descriptor, bool> edgePair = edge(sv, *f_vi, fg); Graph::edge_descriptor edge = edgePair.first; DEBUG("history.find", "u commodity = " << get(namemap, sv)->symbol()); DEBUG("history.find", "v commodity = " << get(namemap, *f_vi)->symbol()); const price_point_t& point(get(pricemap, edge)); if (price.is_null() || point.when > most_recent) { most_recent = point.when; price = point.price; } DEBUG("history.find", "price was = " << price.unrounded()); if (price.commodity() == source) { price.in_place_invert(); if (source == *get(namemap, sv)) price.set_commodity(const_cast<commodity_t&>(*get(namemap, *f_vi))); else price.set_commodity(const_cast<commodity_t&>(*get(namemap, sv))); } DEBUG("history.find", "price is = " << price.unrounded()); } if (price.is_null()) { DEBUG("history.find", "there is no final price"); return none; } else { DEBUG("history.find", "final price is = " << price.unrounded()); return price_point_t(most_recent, price); } } optional<price_point_t> commodity_history_impl_t::find_price(const commodity_t& source, const commodity_t& target, const datetime_t& moment, const datetime_t& oldest) { assert(source != target); vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); FGraph fg(price_graph, recent_edge_weight<EdgeWeightMap, PricePointMap, PriceRatioMap> (get(edge_weight, price_graph), pricemap, ratiomap, moment, oldest)); FNameMap namemap(get(vertex_name, fg)); DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); DEBUG("history.find", "tv commodity = " << get(namemap, tv)->symbol()); std::size_t vector_len(num_vertices(fg)); std::vector<vertex_descriptor> predecessors(vector_len); std::vector<long> distances(vector_len); FPredecessorMap predecessorMap(&predecessors[0]); FDistanceMap distanceMap(&distances[0]); dijkstra_shortest_paths(fg, /* start= */ sv, predecessor_map(predecessorMap) .distance_map(distanceMap) .distance_combine(f_max<long>())); // Extract the shortest path and performance the calculations datetime_t least_recent = moment; amount_t price; const commodity_t * last_target = ⌖ #if defined(REVERSE_PREDECESSOR_MAP) typedef tuple<const commodity_t *, const commodity_t *, const price_point_t *> results_tuple; std::vector<results_tuple> results; bool results_reversed = false; #endif vertex_descriptor v = tv; for (vertex_descriptor u = predecessorMap[v]; u != v; v = u, u = predecessorMap[v]) { std::pair<Graph::edge_descriptor, bool> edgePair_uv = edge(u, v, fg); std::pair<Graph::edge_descriptor, bool> edgePair_vu = edge(v, u, fg); Graph::edge_descriptor edge_uv = edgePair_uv.first; Graph::edge_descriptor edge_vu = edgePair_vu.first; const price_point_t& point_uv(get(pricemap, edge_uv)); const price_point_t& point_vu(get(pricemap, edge_vu)); const price_point_t& point(point_vu.when > point_uv.when ? point_vu : point_uv); const commodity_t * u_comm = get(namemap, u); const commodity_t * v_comm = get(namemap, v); #if defined(REVERSE_PREDECESSOR_MAP) if (v == tv && u_comm != last_target && v_comm != last_target) results_reversed = true; results.push_back(results_tuple(u_comm, v_comm, &point)); } if (results_reversed) std::reverse(results.begin(), results.end()); foreach (const results_tuple& edge, results) { const commodity_t * u_comm = edge.get<0>(); const commodity_t * v_comm = edge.get<1>(); const price_point_t& point(*edge.get<2>()); #else assert(u_comm == last_target || v_comm == last_target); #endif bool first_run = false; if (price.is_null()) { least_recent = point.when; first_run = true; } else if (point.when < least_recent) { least_recent = point.when; } DEBUG("history.find", "u commodity = " << u_comm->symbol()); DEBUG("history.find", "v commodity = " << v_comm->symbol()); DEBUG("history.find", "last target = " << last_target->symbol()); // Determine which direction we are converting in amount_t pprice(point.price); DEBUG("history.find", "pprice = " << pprice.unrounded()); if (! first_run) { DEBUG("history.find", "price was = " << price.unrounded()); if (pprice.commodity_ptr() != last_target) price *= pprice.inverted(); else price *= pprice; } else if (pprice.commodity_ptr() != last_target) { price = pprice.inverted(); } else { price = pprice; } DEBUG("history.find", "price is = " << price.unrounded()); if (last_target == v_comm) last_target = u_comm; else last_target = v_comm; DEBUG("history.find", "last target now = " << last_target->symbol()); } if (price.is_null()) { DEBUG("history.find", "there is no final price"); return none; } else { price.set_commodity(const_cast<commodity_t&>(target)); DEBUG("history.find", "final price is = " << price.unrounded()); return price_point_t(least_recent, price); } } template <class Name> class label_writer { public: label_writer(Name _name) : name(_name) { TRACE_CTOR(label_writer<Name>, "Name"); } ~label_writer() throw() { TRACE_DTOR(label_writer<Name>); } template <class VertexOrEdge> void operator()(std::ostream& out, const VertexOrEdge& v) const { out << "[label=\"" << name[v]->symbol() << "\"]"; } private: Name name; }; void commodity_history_impl_t::print_map(std::ostream& out, const datetime_t& moment) { if (moment.is_not_a_date_time()) { write_graphviz(out, price_graph, label_writer<NameMap>(get(vertex_name, price_graph))); } else { FGraph fg(price_graph, recent_edge_weight<EdgeWeightMap, PricePointMap, PriceRatioMap> (get(edge_weight, price_graph), pricemap, ratiomap, moment)); write_graphviz(out, fg, label_writer<FNameMap>(get(vertex_name, fg))); } } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/history.h��������������������������������������������������������������������������0000664�0000000�0000000�00000006200�14411236400�0015413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup math */ /** * @file history.h * @author John Wiegley * * @ingroup math * * @brief Types for managing commodity historys * * Long. */ #ifndef INCLUDED_HISTORY_H #define INCLUDED_HISTORY_H #include "amount.h" #include "commodity.h" namespace ledger { typedef std::map<datetime_t, amount_t> price_map_t; class commodity_history_impl_t; class commodity_history_t : public noncopyable { unique_ptr<commodity_history_impl_t> p_impl; public: commodity_history_t(); void add_commodity(commodity_t& comm); void add_price(const commodity_t& source, const datetime_t& when, const amount_t& price); void remove_price(const commodity_t& source, const commodity_t& target, const datetime_t& date); void map_prices(function<void(datetime_t, const amount_t&)> fn, const commodity_t& source, const datetime_t& moment, const datetime_t& _oldest = datetime_t(), bool bidirectionally = false); boost::optional<price_point_t> find_price(const commodity_t& source, const datetime_t& moment, const datetime_t& oldest = datetime_t()); boost::optional<price_point_t> find_price(const commodity_t& source, const commodity_t& target, const datetime_t& moment, const datetime_t& oldest = datetime_t()); void print_map(std::ostream& out, const datetime_t& moment = datetime_t()); ~commodity_history_t(); }; } // namespace ledger #endif // INCLUDED_HISTORY_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/item.cc����������������������������������������������������������������������������0000664�0000000�0000000�00000040662�14411236400�0015020�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "item.h" namespace ledger { bool item_t::use_aux_date = false; bool item_t::has_tag(const string& tag, bool) const { DEBUG("item.meta", "Checking if item has tag: " << tag); if (! metadata) { DEBUG("item.meta", "Item has no metadata at all"); return false; } string_map::const_iterator i = metadata->find(tag); #if DEBUG_ON if (SHOW_DEBUG("item.meta")) { if (i == metadata->end()) DEBUG("item.meta", "Item does not have this tag"); else DEBUG("item.meta", "Item has the tag!"); } #endif return i != metadata->end(); } bool item_t::has_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask, bool) const { if (metadata) { foreach (const string_map::value_type& data, *metadata) { if (tag_mask.match(data.first)) { if (! value_mask) return true; else if (data.second.first) return value_mask->match(data.second.first->to_string()); } } } return false; } optional<value_t> item_t::get_tag(const string& tag, bool) const { DEBUG("item.meta", "Getting item tag: " << tag); if (metadata) { DEBUG("item.meta", "Item has metadata"); string_map::const_iterator i = metadata->find(tag); if (i != metadata->end()) { DEBUG("item.meta", "Found the item!"); return (*i).second.first; } } return none; } optional<value_t> item_t::get_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask, bool) const { if (metadata) { foreach (const string_map::value_type& data, *metadata) { if (tag_mask.match(data.first) && (! value_mask || (data.second.first && value_mask->match(data.second.first->to_string())))) { return data.second.first; } } } return none; } namespace { struct CaseInsensitiveKeyCompare : public std::binary_function<string, string, bool> { bool operator()(const string& s1, const string& s2) const { return boost::algorithm::ilexicographical_compare(s1, s2); } }; } item_t::string_map::iterator item_t::set_tag(const string& tag, const optional<value_t>& value, const bool overwrite_existing) { assert(! tag.empty()); if (! metadata) metadata = string_map(CaseInsensitiveKeyCompare()); DEBUG("item.meta", "Setting tag '" << tag << "' to value '" << (value ? *value : string_value("<none>")) << "'"); optional<value_t> data = value; if (data && (data->is_null() || (data->is_string() && data->as_string().empty()))) data = none; string_map::iterator i = metadata->find(tag); if (i == metadata->end()) { std::pair<string_map::iterator, bool> result = metadata->insert(string_map::value_type(tag, tag_data_t(data, false))); assert(result.second); return result.first; } else { if (overwrite_existing) (*i).second = tag_data_t(data, false); return i; } } void item_t::parse_tags(const char * p, scope_t& scope, bool overwrite_existing) { if (! std::strchr(p, ':')) { if (const char * b = std::strchr(p, '[')) { if (*(b + 1) != '\0' && (std::isdigit(*(b + 1)) || *(b + 1) == '=')) { if (const char * e = std::strchr(b, ']')) { char buf[256]; std::strncpy(buf, b + 1, static_cast<std::size_t>(e - b - 1)); buf[e - b - 1] = '\0'; if (char * pp = std::strchr(buf, '=')) { *pp++ = '\0'; _date_aux = parse_date(pp); } if (buf[0]) _date = parse_date(buf); } } } return; } scoped_array<char> buf(new char[std::strlen(p) + 1]); std::strcpy(buf.get(), p); string tag; bool by_value = false; bool first = true; for (char * q = std::strtok(buf.get(), " \t"); q; q = std::strtok(NULL, " \t")) { const string::size_type len = std::strlen(q); if (len < 2) continue; if (q[0] == ':' && q[len - 1] == ':') { // a series of tags for (char * r = std::strtok(q + 1, ":"); r; r = std::strtok(NULL, ":")) { string_map::iterator i = set_tag(r, none, overwrite_existing); (*i).second.second = true; } } else if (first && q[len - 1] == ':') { // a metadata setting std::size_t index = 1; if (q[len - 2] == ':') { by_value = true; index = 2; } tag = string(q, len - index); string_map::iterator i; string field(p + (q - buf.get()) + len); trim(field); if (by_value) { bind_scope_t bound_scope(scope, *this); i = set_tag(tag, expr_t(field).calc(bound_scope), overwrite_existing); } else { i = set_tag(tag, string_value(field), overwrite_existing); } (*i).second.second = true; break; } first = false; } } void item_t::append_note(const char * p, scope_t& scope, bool overwrite_existing) { if (note) { *note += '\n'; *note += p; } else { note = p; } parse_tags(p, scope, overwrite_existing); } namespace { value_t get_status(item_t& item) { return long(item.state()); } value_t get_uncleared(item_t& item) { return item.state() == item_t::UNCLEARED; } value_t get_cleared(item_t& item) { return item.state() == item_t::CLEARED; } value_t get_pending(item_t& item) { return item.state() == item_t::PENDING; } value_t get_actual(item_t& item) { return ! item.has_flags(ITEM_GENERATED | ITEM_TEMP); } value_t get_date(item_t& item) { return item.date(); } value_t get_primary_date(item_t& item) { return item.primary_date(); } value_t get_aux_date(item_t& item) { if (optional<date_t> aux_date = item.aux_date()) return *aux_date; return NULL_VALUE; } value_t get_note(item_t& item) { return item.note ? string_value(*item.note) : NULL_VALUE; } value_t has_tag(call_scope_t& args) { item_t& item(find_scope<item_t>(args)); if (args.size() == 1) { if (args[0].is_string()) return item.has_tag(args.get<string>(0)); else if (args[0].is_mask()) return item.has_tag(args.get<mask_t>(0)); else throw_(std::runtime_error, _f("Expected string or mask for argument 1, but received %1%") % args[0].label()); } else if (args.size() == 2) { if (args[0].is_mask() && args[1].is_mask()) return item.has_tag(args.get<mask_t>(0), args.get<mask_t>(1)); else throw_(std::runtime_error, _f("Expected masks for arguments 1 and 2, but received %1% and %2%") % args[0].label() % args[1].label()); } else if (args.size() == 0) { throw_(std::runtime_error, _("Too few arguments to function")); } else { throw_(std::runtime_error, _("Too many arguments to function")); } return false; } value_t get_tag(call_scope_t& args) { item_t& item(find_scope<item_t>(args)); optional<value_t> val; if (args.size() == 1) { if (args[0].is_string()) val = item.get_tag(args.get<string>(0)); else if (args[0].is_mask()) val = item.get_tag(args.get<mask_t>(0)); else throw_(std::runtime_error, _f("Expected string or mask for argument 1, but received %1%") % args[0].label()); } else if (args.size() == 2) { if (args[0].is_mask() && args[1].is_mask()) val = item.get_tag(args.get<mask_t>(0), args.get<mask_t>(1)); else throw_(std::runtime_error, _f("Expected masks for arguments 1 and 2, but received %1% and %2%") % args[0].label() % args[1].label()); } else if (args.size() == 0) { throw_(std::runtime_error, _("Too few arguments to function")); } else { throw_(std::runtime_error, _("Too many arguments to function")); } return val ? *val : NULL_VALUE; } value_t get_pathname(item_t& item) { if (item.pos) return string_value(item.pos->pathname.string()); else return NULL_VALUE; } value_t get_filebase(item_t& item) { if (item.pos) return string_value(item.pos->pathname.filename().string()); else return NULL_VALUE; } value_t get_filepath(item_t& item) { if (item.pos) return string_value(item.pos->pathname.parent_path().string()); else return NULL_VALUE; } value_t get_beg_pos(item_t& item) { return item.pos ? long(item.pos->beg_pos) : 0L; } value_t get_beg_line(item_t& item) { return item.pos ? long(item.pos->beg_line) : 0L; } value_t get_end_pos(item_t& item) { return item.pos ? long(item.pos->end_pos) : 0L; } value_t get_end_line(item_t& item) { return item.pos ? long(item.pos->end_line) : 0L; } value_t get_seq(item_t& item) { return long(item.seq()); } value_t get_id(item_t& item) { return string_value(item.id()); } value_t get_addr(item_t& item) { return long(reinterpret_cast<intptr_t>(&item)); } value_t get_depth(item_t&) { return 0L; } value_t ignore(item_t&) { return false; } template <value_t (*Func)(item_t&)> value_t get_wrapper(call_scope_t& scope) { return (*Func)(find_scope<item_t>(scope)); } } value_t get_comment(item_t& item) { if (! item.note) { return string_value(""); } else { std::ostringstream buf; if (item.note->length() > 15) buf << "\n ;"; else buf << " ;"; bool need_separator = false; for (const char * p = item.note->c_str(); *p; p++) { if (*p == '\n') { need_separator = true; } else { if (need_separator) { buf << "\n ;"; need_separator = false; } buf << *p; } } return string_value(buf.str()); } } void item_t::define(const symbol_t::kind_t, const string& name, expr_t::ptr_op_t def) { bind_scope_t bound_scope(*scope_t::default_scope, *this); set_tag(name, def->calc(bound_scope)); } expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, const string& name) { if (kind != symbol_t::FUNCTION) return NULL; switch (name[0]) { case 'a': if (name == "actual") return WRAP_FUNCTOR(get_wrapper<&get_actual>); else if (name == "actual_date") return WRAP_FUNCTOR(get_wrapper<&get_primary_date>); else if (name == "addr") return WRAP_FUNCTOR(get_wrapper<&get_addr>); else if (name == "aux_date") return WRAP_FUNCTOR(get_wrapper<&get_aux_date>); break; case 'b': if (name == "beg_line") return WRAP_FUNCTOR(get_wrapper<&get_beg_line>); else if (name == "beg_pos") return WRAP_FUNCTOR(get_wrapper<&get_beg_pos>); break; case 'c': if (name == "cleared") return WRAP_FUNCTOR(get_wrapper<&get_cleared>); else if (name == "comment") return WRAP_FUNCTOR(get_wrapper<&get_comment>); break; case 'd': if (name[1] == '\0' || name == "date") return WRAP_FUNCTOR(get_wrapper<&get_date>); else if (name == "depth") return WRAP_FUNCTOR(get_wrapper<&get_depth>); break; case 'e': if (name == "end_line") return WRAP_FUNCTOR(get_wrapper<&get_end_line>); else if (name == "end_pos") return WRAP_FUNCTOR(get_wrapper<&get_end_pos>); else if (name == "effective_date") return WRAP_FUNCTOR(get_wrapper<&get_aux_date>); break; case 'f': if (name == "filename") return WRAP_FUNCTOR(get_wrapper<&get_pathname>); else if (name == "filebase") return WRAP_FUNCTOR(get_wrapper<&get_filebase>); else if (name == "filepath") return WRAP_FUNCTOR(get_wrapper<&get_filepath>); break; case 'h': if (name == "has_tag") return WRAP_FUNCTOR(ledger::has_tag); else if (name == "has_meta") return WRAP_FUNCTOR(ledger::has_tag); break; case 'i': if (name == "is_account") return WRAP_FUNCTOR(get_wrapper<&ignore>); else if (name == "id") return WRAP_FUNCTOR(get_wrapper<&get_id>); break; case 'm': if (name == "meta") return WRAP_FUNCTOR(ledger::get_tag); break; case 'n': if (name == "note") return WRAP_FUNCTOR(get_wrapper<&get_note>); break; case 'p': if (name == "pending") return WRAP_FUNCTOR(get_wrapper<&get_pending>); else if (name == "parent") return WRAP_FUNCTOR(get_wrapper<&ignore>); else if (name == "primary_date") return WRAP_FUNCTOR(get_wrapper<&get_primary_date>); break; case 's': if (name == "status" || name == "state") return WRAP_FUNCTOR(get_wrapper<&get_status>); else if (name == "seq") return WRAP_FUNCTOR(get_wrapper<&get_seq>); break; case 't': if (name == "tag") return WRAP_FUNCTOR(ledger::get_tag); break; case 'u': if (name == "uncleared") return WRAP_FUNCTOR(get_wrapper<&get_uncleared>); else if (name == "uuid") return WRAP_FUNCTOR(get_wrapper<&get_id>); break; case 'v': if (name == "value_date") return WRAP_FUNCTOR(get_wrapper<&get_date>); break; case 'L': if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_actual>); break; case 'X': if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_cleared>); break; case 'Y': if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_pending>); break; } return NULL; } bool item_t::valid() const { if (_state != UNCLEARED && _state != CLEARED && _state != PENDING) { DEBUG("ledger.validate", "item_t: state is bad"); return false; } return true; } void print_item(std::ostream& out, const item_t& item, const string& prefix) { out << source_context(item.pos->pathname, item.pos->beg_pos, item.pos->end_pos, prefix); } string item_context(const item_t& item, const string& desc) { if (! item.pos) return empty_string; std::streamoff len = item.pos->end_pos - item.pos->beg_pos; if (! (len > 0)) return empty_string; assert(len < 1024 * 1024); std::ostringstream out; if (item.pos->pathname.empty()) { out << desc << _(" from streamed input:"); return out.str(); } out << desc << _(" from \"") << item.pos->pathname.string() << "\""; if (item.pos->beg_line != item.pos->end_line) out << _(", lines ") << item.pos->beg_line << "-" << item.pos->end_line << ":\n"; else out << _(", line ") << item.pos->beg_line << ":\n"; print_item(out, item, "> "); return out.str(); } void put_metadata(property_tree::ptree& st, const item_t::string_map& metadata) { foreach (const item_t::string_map::value_type& pair, metadata) { if (pair.second.first) { property_tree::ptree& vt(st.add("value", "")); vt.put("<xmlattr>.key", pair.first); put_value(vt, *pair.second.first); } else { st.add("tag", pair.first); } } } } // namespace ledger ������������������������������������������������������������������������������ledger-3.3.2/src/item.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000015150�14411236400�0014654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @defgroup data Data representation */ /** * @file item.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_ITEM_H #define INCLUDED_ITEM_H #include "scope.h" namespace ledger { struct position_t { path pathname; std::istream::pos_type beg_pos; std::size_t beg_line; std::istream::pos_type end_pos; std::size_t end_line; std::size_t sequence; position_t() : beg_pos(0), beg_line(0), end_pos(0), end_line(0), sequence(0) { TRACE_CTOR(position_t, ""); } position_t(const position_t& pos) { *this = pos; TRACE_CTOR(position_t, "copy"); } ~position_t() throw() { TRACE_DTOR(position_t); } position_t& operator=(const position_t& pos) { if (this != &pos) { pathname = pos.pathname; beg_pos = pos.beg_pos; beg_line = pos.beg_line; end_pos = pos.end_pos; end_line = pos.end_line; sequence = pos.sequence; } return *this; } }; class item_t : public supports_flags<uint_least16_t>, public scope_t { public: #define ITEM_NORMAL 0x00 // no flags at all, a basic posting #define ITEM_GENERATED 0x01 // posting was not found in a journal #define ITEM_TEMP 0x02 // posting is a managed temporary #define ITEM_NOTE_ON_NEXT_LINE 0x04 // did we see a note on the next line? enum state_t { UNCLEARED = 0, CLEARED, PENDING }; typedef std::pair<optional<value_t>, bool> tag_data_t; typedef std::map<string, tag_data_t, std::function<bool(string, string)> > string_map; state_t _state; optional<date_t> _date; optional<date_t> _date_aux; optional<string> note; optional<position_t> pos; optional<string_map> metadata; item_t(flags_t _flags = ITEM_NORMAL, const optional<string>& _note = none) : supports_flags<uint_least16_t>(_flags), _state(UNCLEARED), note(_note) { TRACE_CTOR(item_t, "flags_t, const string&"); } item_t(const item_t& item) : supports_flags<uint_least16_t>(), scope_t() { copy_details(item); TRACE_CTOR(item_t, "copy"); } virtual ~item_t() { TRACE_DTOR(item_t); } virtual void copy_details(const item_t& item) { set_flags(item.flags()); set_state(item.state()); _date = item._date; _date_aux = item._date_aux; note = item.note; pos = item.pos; metadata = item.metadata; } virtual bool operator==(const item_t& xact) { return this == &xact; } virtual bool operator!=(const item_t& xact) { return ! (*this == xact); } string id() const { if (optional<value_t> ref = get_tag(_("UUID"))) { return ref->to_string(); } else { std::ostringstream buf; buf << seq(); return buf.str(); } } std::size_t seq() const { return pos ? pos->sequence : 0L; } virtual bool has_tag(const string& tag, bool inherit = true) const; virtual bool has_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask = none, bool inherit = true) const; virtual optional<value_t> get_tag(const string& tag, bool inherit = true) const; virtual optional<value_t> get_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask = none, bool inherit = true) const; virtual string_map::iterator set_tag(const string& tag, const optional<value_t>& value = none, const bool overwrite_existing = true); virtual void parse_tags(const char * p, scope_t& scope, bool overwrite_existing = true); virtual void append_note(const char * p, scope_t& scope, bool overwrite_existing = true); static bool use_aux_date; virtual bool has_date() const { return static_cast<bool>(_date); } virtual date_t date() const { assert(_date); if (use_aux_date) if (optional<date_t> aux = aux_date()) return *aux; return *_date; } virtual date_t primary_date() const { assert(_date); return *_date; } virtual optional<date_t> aux_date() const { return _date_aux; } void set_state(state_t new_state) { _state = new_state; } virtual state_t state() const { return _state; } virtual void define(const symbol_t::kind_t, const string&, expr_t::ptr_op_t); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); bool valid() const; }; value_t get_comment(item_t& item); void print_item(std::ostream& out, const item_t& item, const string& prefix = ""); string item_context(const item_t& item, const string& desc); void put_metadata(property_tree::ptree& pt, const item_t::string_map& metadata); } // namespace ledger #endif // INCLUDED_ITEM_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/iterators.cc�����������������������������������������������������������������������0000664�0000000�0000000�00000016631�14411236400�0016075�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "iterators.h" #include "journal.h" #include "compare.h" namespace ledger { void xacts_iterator::reset(journal_t& journal) { xacts_i = journal.xacts.begin(); xacts_end = journal.xacts.end(); xacts_uninitialized = false; increment(); } void xacts_iterator::increment() { if (xacts_i != xacts_end) m_node = *xacts_i++; else m_node = NULL; } void journal_posts_iterator::reset(journal_t& journal) { xacts.reset(journal); increment(); } void journal_posts_iterator::increment() { if (post_t * post = *posts++) { m_node = post; } else if (xact_t * xact = *xacts++) { posts.reset(*xact); m_node = *posts++; } else { m_node = NULL; } } namespace { struct create_price_xact { journal_t& journal; account_t * account; temporaries_t& temps; xacts_list& xact_temps; std::map<string, xact_t *> xacts_by_commodity; create_price_xact(journal_t& _journal, account_t * _account, temporaries_t& _temps, xacts_list& _xact_temps) : journal(_journal), account(_account), temps(_temps), xact_temps(_xact_temps) { TRACE_CTOR(create_price_xact, "journal_t&, account_t *, temporaries_t&, xacts_list&"); } ~create_price_xact() throw() { TRACE_DTOR(create_price_xact); } void operator()(const datetime_t& date, const amount_t& price) { xact_t * xact; string symbol = price.commodity().symbol(); std::map<string, xact_t *>::iterator i = xacts_by_commodity.find(symbol); if (i != xacts_by_commodity.end()) { xact = (*i).second; } else { xact = &temps.create_xact(); xact_temps.push_back(xact); xact->payee = symbol; xact->_date = date.date(); xacts_by_commodity.insert (std::pair<string, xact_t *>(symbol, xact)); xact->journal = &journal; } bool post_already_exists = false; foreach (post_t * post, xact->posts) { if (post->date() == date.date() && post->amount == price) { post_already_exists = true; break; } } if (! post_already_exists) { post_t& temp = temps.create_post(*xact, account); temp._date = date.date(); temp.amount = price; temp.xdata().datetime = date; } } }; } void posts_commodities_iterator::reset(journal_t& journal) { journal_posts.reset(journal); std::set<commodity_t *> commodities; while (const post_t * post = *journal_posts++) { commodity_t& comm(post->amount.commodity()); if (comm.flags() & COMMODITY_NOMARKET) continue; commodities.insert(&comm.referent()); } foreach (commodity_t * comm, commodities) comm->map_prices (create_price_xact(journal, journal.master->find_account(comm->symbol()), temps, xact_temps)); xacts.reset(xact_temps.begin(), xact_temps.end()); increment(); } void posts_commodities_iterator::increment() { if (post_t * post = *posts++) { m_node = post; } else if (xact_t * xact = *xacts++) { posts.reset(*xact); m_node = *posts++; } else { m_node = NULL; } } void basic_accounts_iterator::increment() { while (! accounts_i.empty() && accounts_i.back() == accounts_end.back()) { accounts_i.pop_back(); accounts_end.pop_back(); } if (accounts_i.empty()) { m_node = NULL; } else { account_t * account = (*(accounts_i.back()++)).second; assert(account); // If this account has children, queue them up to be iterated next. if (! account->accounts.empty()) push_back(*account); m_node = account; } } void sorted_accounts_iterator::push_back(account_t& account) { accounts_list.push_back(accounts_deque_t()); if (flatten_all) { push_all(account, accounts_list.back()); std::stable_sort(accounts_list.back().begin(), accounts_list.back().end(), compare_items<account_t>(sort_cmp, report)); #if DEBUG_ON if (SHOW_DEBUG("account.sorted")) { foreach (account_t * acct, accounts_list.back()) DEBUG("account.sorted", "Account (flat): " << acct->fullname()); } #endif } else { sort_accounts(account, accounts_list.back()); } sorted_accounts_i.push_back(accounts_list.back().begin()); sorted_accounts_end.push_back(accounts_list.back().end()); } void sorted_accounts_iterator::push_all(account_t& account, accounts_deque_t& deque) { foreach (accounts_map::value_type& pair, account.accounts) { deque.push_back(pair.second); push_all(*pair.second, deque); } } void sorted_accounts_iterator::sort_accounts(account_t& account, accounts_deque_t& deque) { foreach (accounts_map::value_type& pair, account.accounts) deque.push_back(pair.second); std::stable_sort(deque.begin(), deque.end(), compare_items<account_t>(sort_cmp, report)); #if DEBUG_ON if (SHOW_DEBUG("account.sorted")) { foreach (account_t * acct, deque) DEBUG("account.sorted", "Account: " << acct->fullname()); } #endif } void sorted_accounts_iterator::increment() { while (! sorted_accounts_i.empty() && sorted_accounts_i.back() == sorted_accounts_end.back()) { sorted_accounts_i.pop_back(); sorted_accounts_end.pop_back(); assert(! accounts_list.empty()); accounts_list.pop_back(); } if (sorted_accounts_i.empty()) { m_node = NULL; } else { account_t * account = *sorted_accounts_i.back()++; assert(account); // If this account has children, queue them up to be iterated next. if (! flatten_all && ! account->accounts.empty()) push_back(*account); // Make sure the sorting value gets recalculated for this account account->xdata().drop_flags(ACCOUNT_EXT_SORT_CALC); m_node = account; } } } // namespace ledger �������������������������������������������������������������������������������������������������������ledger-3.3.2/src/iterators.h������������������������������������������������������������������������0000664�0000000�0000000�00000023233�14411236400�0015733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file iterators.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_ITERATORS_H #define INCLUDED_ITERATORS_H #include "xact.h" #include "post.h" #include "account.h" #include "temps.h" namespace ledger { class journal_t; class report_t; template <typename Derived, typename Value, typename CategoryOrTraversal> class iterator_facade_base : public boost::iterator_facade<Derived, Value, CategoryOrTraversal> { typedef Value node_base; public: iterator_facade_base() : m_node(NULL) { TRACE_CTOR(iterator_facade_base, ""); } iterator_facade_base(const iterator_facade_base& i) : m_node(i.m_node) { TRACE_CTOR(iterator_facade_base, "copy"); } ~iterator_facade_base() throw() { TRACE_DTOR(iterator_facade_base); } explicit iterator_facade_base(node_base p) : m_node(p) {} void increment(); private: friend class boost::iterator_core_access; bool equal(iterator_facade_base const& other) const { return this->m_node == other.m_node; } node_base& dereference() const { return const_cast<node_base&>(m_node); } protected: node_base m_node; }; class xact_posts_iterator : public iterator_facade_base<xact_posts_iterator, post_t *, boost::forward_traversal_tag> { posts_list::iterator posts_i; posts_list::iterator posts_end; bool posts_uninitialized; public: xact_posts_iterator() : posts_uninitialized(true) { TRACE_CTOR(xact_posts_iterator, ""); } xact_posts_iterator(xact_t& xact) : posts_uninitialized(true) { reset(xact); TRACE_CTOR(xact_posts_iterator, "xact_t&"); } xact_posts_iterator(const xact_posts_iterator& i) : iterator_facade_base<xact_posts_iterator, post_t *, boost::forward_traversal_tag>(i), posts_i(i.posts_i), posts_end(i.posts_end), posts_uninitialized(i.posts_uninitialized) { TRACE_CTOR(xact_posts_iterator, "copy"); } ~xact_posts_iterator() throw() { TRACE_DTOR(xact_posts_iterator); } void reset(xact_t& xact) { posts_i = xact.posts.begin(); posts_end = xact.posts.end(); posts_uninitialized = false; increment(); } void increment() { if (posts_uninitialized || posts_i == posts_end) m_node = NULL; else m_node = *posts_i++; } }; class xacts_iterator : public iterator_facade_base<xacts_iterator, xact_t *, boost::forward_traversal_tag> { public: xacts_list::iterator xacts_i; xacts_list::iterator xacts_end; bool xacts_uninitialized; xacts_iterator() : xacts_uninitialized(true) { TRACE_CTOR(xacts_iterator, ""); } xacts_iterator(journal_t& journal) : xacts_uninitialized(false) { reset(journal); TRACE_CTOR(xacts_iterator, "journal_t&"); } xacts_iterator(xacts_list::iterator beg, xacts_list::iterator end) : xacts_uninitialized(false) { reset(beg, end); TRACE_CTOR(xacts_iterator, "xacts_list::iterator, xacts_list::iterator"); } xacts_iterator(const xacts_iterator& i) : iterator_facade_base<xacts_iterator, xact_t *, boost::forward_traversal_tag>(i), xacts_i(i.xacts_i), xacts_end(i.xacts_end), xacts_uninitialized(i.xacts_uninitialized) { TRACE_CTOR(xacts_iterator, "copy"); } ~xacts_iterator() throw() { TRACE_DTOR(xacts_iterator); } void reset(journal_t& journal); void reset(xacts_list::iterator beg, xacts_list::iterator end) { xacts_i = beg; xacts_end = end; increment(); } void increment(); }; class journal_posts_iterator : public iterator_facade_base<journal_posts_iterator, post_t *, boost::forward_traversal_tag> { xacts_iterator xacts; xact_posts_iterator posts; public: journal_posts_iterator() { TRACE_CTOR(journal_posts_iterator, ""); } journal_posts_iterator(journal_t& journal) { reset(journal); TRACE_CTOR(journal_posts_iterator, "journal_t&"); } journal_posts_iterator(const journal_posts_iterator& i) : iterator_facade_base<journal_posts_iterator, post_t *, boost::forward_traversal_tag>(i), xacts(i.xacts), posts(i.posts) { TRACE_CTOR(journal_posts_iterator, "copy"); } ~journal_posts_iterator() throw() { TRACE_DTOR(journal_posts_iterator); } void reset(journal_t& journal); void increment(); }; class posts_commodities_iterator : public iterator_facade_base<posts_commodities_iterator, post_t *, boost::forward_traversal_tag> { protected: journal_posts_iterator journal_posts; xacts_iterator xacts; xact_posts_iterator posts; xacts_list xact_temps; temporaries_t temps; public: posts_commodities_iterator() { TRACE_CTOR(posts_commodities_iterator, ""); } posts_commodities_iterator(journal_t& journal) { reset(journal); TRACE_CTOR(posts_commodities_iterator, "journal_t&"); } posts_commodities_iterator(const posts_commodities_iterator& i) : iterator_facade_base<posts_commodities_iterator, post_t *, boost::forward_traversal_tag>(i), journal_posts(i.journal_posts), xacts(i.xacts), posts(i.posts), xact_temps(i.xact_temps), temps(i.temps) { TRACE_CTOR(posts_commodities_iterator, "copy"); } ~posts_commodities_iterator() throw() { TRACE_DTOR(posts_commodities_iterator); } void reset(journal_t& journal); void increment(); }; class basic_accounts_iterator : public iterator_facade_base<basic_accounts_iterator, account_t *, boost::forward_traversal_tag> { std::list<accounts_map::const_iterator> accounts_i; std::list<accounts_map::const_iterator> accounts_end; public: basic_accounts_iterator() { TRACE_CTOR(basic_accounts_iterator, ""); } basic_accounts_iterator(account_t& account) { push_back(account); increment(); TRACE_CTOR(basic_accounts_iterator, "account_t&"); } basic_accounts_iterator(const basic_accounts_iterator& i) : iterator_facade_base<basic_accounts_iterator, account_t *, boost::forward_traversal_tag>(i), accounts_i(i.accounts_i), accounts_end(i.accounts_end) { TRACE_CTOR(basic_accounts_iterator, "copy"); } ~basic_accounts_iterator() throw() { TRACE_DTOR(basic_accounts_iterator); } void increment(); private: void push_back(account_t& account) { accounts_i.push_back(account.accounts.begin()); accounts_end.push_back(account.accounts.end()); } }; class sorted_accounts_iterator : public iterator_facade_base<sorted_accounts_iterator, account_t *, boost::forward_traversal_tag> { expr_t sort_cmp; report_t& report; bool flatten_all; typedef std::deque<account_t *> accounts_deque_t; std::list<accounts_deque_t> accounts_list; std::list<accounts_deque_t::const_iterator> sorted_accounts_i; std::list<accounts_deque_t::const_iterator> sorted_accounts_end; public: sorted_accounts_iterator(account_t& account, const expr_t& _sort_cmp, report_t& _report, bool _flatten_all) : sort_cmp(_sort_cmp), report(_report), flatten_all(_flatten_all) { push_back(account); increment(); TRACE_CTOR(sorted_accounts_iterator, "account_t&, expr_t, report_t&, bool"); } sorted_accounts_iterator(const sorted_accounts_iterator& i) : iterator_facade_base<sorted_accounts_iterator, account_t *, boost::forward_traversal_tag>(i), sort_cmp(i.sort_cmp), report(i.report), flatten_all(i.flatten_all), accounts_list(i.accounts_list), sorted_accounts_i(i.sorted_accounts_i), sorted_accounts_end(i.sorted_accounts_end) { TRACE_CTOR(sorted_accounts_iterator, "copy"); } ~sorted_accounts_iterator() throw() { TRACE_DTOR(sorted_accounts_iterator); } void increment(); private: void push_back(account_t& account); void push_all(account_t& account, accounts_deque_t& deque); void sort_accounts(account_t& account, accounts_deque_t& deque); }; } // namespace ledger #endif // INCLUDED_ITERATORS_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/journal.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000040766�14411236400�0015541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "journal.h" #include "context.h" #include "amount.h" #include "commodity.h" #include "pool.h" #include "xact.h" #include "post.h" #include "account.h" namespace ledger { journal_t::journal_t() { initialize(); TRACE_CTOR(journal_t, ""); } #if 0 journal_t::journal_t(const path& pathname) { initialize(); read(pathname); TRACE_CTOR(journal_t, "path"); } journal_t::journal_t(const string& str) { initialize(); read(str); TRACE_CTOR(journal_t, "string"); } #endif journal_t::~journal_t() { TRACE_DTOR(journal_t); // Don't bother unhooking each xact's posts from the accounts they refer to, // because all accounts are about to be deleted. foreach (xact_t * xact, xacts) checked_delete(xact); foreach (auto_xact_t * xact, auto_xacts) checked_delete(xact); foreach (period_xact_t * xact, period_xacts) checked_delete(xact); checked_delete(master); } void journal_t::initialize() { master = new account_t; bucket = NULL; current_context = NULL; was_loaded = false; check_payees = false; day_break = false; checking_style = CHECK_NORMAL; recursive_aliases = false; no_aliases = false; } void journal_t::add_account(account_t * acct) { master->add_account(acct); } bool journal_t::remove_account(account_t * acct) { return master->remove_account(acct); } account_t * journal_t::find_account(const string& name, bool auto_create) { return master->find_account(name, auto_create); } account_t * journal_t::find_account_re(const string& regexp) { return master->find_account_re(regexp); } account_t * journal_t::register_account(const string& name, post_t * post, account_t * master_account) { // If there are any account aliases, substitute before creating an account // object. account_t * result = expand_aliases(name); // Create the account object and associate it with the journal; this // is registering the account. if (! result) result = master_account->find_account(name); // If the account name being registered is "Unknown", check whether // the payee indicates an account that should be used. if (result->name == _("Unknown")) { foreach (account_mapping_t& value, payees_for_unknown_accounts) { if (post && post->xact && value.first.match(post->xact->payee)) { result = value.second; break; } } } // Now that we have an account, make certain that the account is // "known", if the user has requested validation of that fact. if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) { if (! result->has_flags(ACCOUNT_KNOWN)) { if (! post) { result->add_flags(ACCOUNT_KNOWN); } else if (checking_style == CHECK_WARNING) { current_context->warning(_f("Unknown account '%1%'") % result->fullname()); } else if (checking_style == CHECK_ERROR) { throw_(parse_error, _f("Unknown account '%1%'") % result->fullname()); } } } return result; } account_t * journal_t::expand_aliases(string name) { // Aliases are expanded recursively, so if both alias Foo=Bar:Foo and // alias Bar=Baaz:Bar are in effect, first Foo will be expanded to Bar:Foo, // then Bar:Foo will be expanded to Baaz:Bar:Foo. // The expansion loop keeps a list of already expanded names in order to // prevent infinite excursion. Each alias may only be expanded at most once. account_t * result = NULL; if (no_aliases) return result; bool keep_expanding = true; std::list<string> already_seen; // loop until no expansion can be found do { if (account_aliases.size() > 0) { accounts_map::const_iterator i = account_aliases.find(name); if (i != account_aliases.end()) { if (std::find(already_seen.begin(), already_seen.end(), name) != already_seen.end()) { throw_(std::runtime_error, _f("Infinite recursion on alias expansion for %1%") % name); } // there is an alias for the full account name, including colons already_seen.push_back(name); result = (*i).second; name = result->fullname(); } else { // only check the very first account for alias expansion, in case // that can be expanded successfully size_t colon = name.find(':'); if (colon != string::npos) { string first_account_name = name.substr(0, colon); accounts_map::const_iterator j = account_aliases.find(first_account_name); if (j != account_aliases.end()) { if (std::find(already_seen.begin(), already_seen.end(), first_account_name) != already_seen.end()) { throw_(std::runtime_error, _f("Infinite recursion on alias expansion for %1%") % first_account_name); } already_seen.push_back(first_account_name); result = find_account((*j).second->fullname() + name.substr(colon)); name = result->fullname(); } else { keep_expanding = false; } } else { keep_expanding = false; } } } else { keep_expanding = false; } } while(keep_expanding && recursive_aliases); return result; } string journal_t::register_payee(const string& name) { if (should_check_payees() && payee_not_registered(name)) { known_payees.insert(name); } return name; } string journal_t::validate_payee(const string& name_or_alias) { string payee = translate_payee_name(name_or_alias); if (should_check_payees() && payee_not_registered(payee)) { if (checking_style == CHECK_WARNING) { current_context->warning(_f("Unknown payee '%1%'") % payee); } else if (checking_style == CHECK_ERROR) { throw_(parse_error, _f("Unknown payee '%1%'") % payee); } } return payee; } bool journal_t::should_check_payees() { return check_payees && (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR); } bool journal_t::payee_not_registered(const string& name) { return known_payees.find(name) == known_payees.end(); } string journal_t::translate_payee_name(const string& name) { string payee; foreach (payee_alias_mapping_t& value, payee_alias_mappings) { if (value.first.match(name)) { payee = value.second; break; } } return payee.empty() ? name : payee; } void journal_t::register_commodity(commodity_t& comm, variant<int, xact_t *, post_t *> context) { if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) { if (! comm.has_flags(COMMODITY_KNOWN)) { if (context.which() == 0) { comm.add_flags(COMMODITY_KNOWN); } else if (checking_style == CHECK_WARNING) { current_context->warning(_f("Unknown commodity '%1%'") % comm); } else if (checking_style == CHECK_ERROR) { throw_(parse_error, _f("Unknown commodity '%1%'") % comm); } } } } void journal_t::register_metadata(const string& key, const value_t& value, variant<int, xact_t *, post_t *> context) { if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) { std::set<string>::iterator i = known_tags.find(key); if (i == known_tags.end()) { if (context.which() == 0) { known_tags.insert(key); } else if (checking_style == CHECK_WARNING) { current_context->warning(_f("Unknown metadata tag '%1%'") % key); } else if (checking_style == CHECK_ERROR) { throw_(parse_error, _f("Unknown metadata tag '%1%'") % key); } } } if (! value.is_null()) { std::pair<tag_check_exprs_map::iterator, tag_check_exprs_map::iterator> range = tag_check_exprs.equal_range(key); for (tag_check_exprs_map::iterator i = range.first; i != range.second; ++i) { bind_scope_t bound_scope (*current_context->scope, context.which() == 1 ? static_cast<scope_t&>(*boost::get<xact_t *>(context)) : static_cast<scope_t&>(*boost::get<post_t *>(context))); value_scope_t val_scope(bound_scope, value); if (! (*i).second.first.calc(val_scope).to_boolean()) { if ((*i).second.second == expr_t::EXPR_ASSERTION) throw_(parse_error, _f("Metadata assertion failed for (%1%: %2%): %3%") % key % value % (*i).second.first); else current_context->warning (_f("Metadata check failed for (%1%: %2%): %3%") % key % value % (*i).second.first); } } } } namespace { void check_all_metadata(journal_t& journal, variant<int, xact_t *, post_t *> context) { xact_t * xact = context.which() == 1 ? boost::get<xact_t *>(context) : NULL; post_t * post = context.which() == 2 ? boost::get<post_t *>(context) : NULL; if ((xact || post) && xact ? xact->metadata : post->metadata) { foreach (const item_t::string_map::value_type& pair, xact ? *xact->metadata : *post->metadata) { const string& key(pair.first); if (optional<value_t> value = pair.second.first) journal.register_metadata(key, *value, context); else journal.register_metadata(key, NULL_VALUE, context); } } } } bool lt_posting_account(post_t * left, post_t * right) { return left->account < right->account; } bool is_equivalent_posting(post_t * left, post_t * right) { if (left->account != right->account) return false; if (left->amount != right->amount) return false; return true; } bool journal_t::add_xact(xact_t * xact) { xact->journal = this; if (! xact->finalize()) { xact->journal = NULL; return false; } extend_xact(xact); check_all_metadata(*this, xact); foreach (post_t * post, xact->posts) { extend_post(*post, *this); check_all_metadata(*this, post); } // If a transaction with this UUID has already been seen, simply do // not add this one to the journal. However, all automated checks // will have been performed by extend_xact, so asserts can still be // applied to it. if (optional<value_t> ref = xact->get_tag(_("UUID"))) { std::string uuid = ref->to_string(); std::pair<checksum_map_t::iterator, bool> result = checksum_map.insert(checksum_map_t::value_type(uuid, xact)); if (! result.second) { // This UUID has been seen before; apply any postings which the // earlier version may have deferred. foreach (post_t * post, xact->posts) { account_t * acct = post->account; if (acct->deferred_posts) { auto i = acct->deferred_posts->find(uuid); if (i != acct->deferred_posts->end()) { for (post_t * rpost : (*i).second) if (acct == rpost->account) acct->add_post(rpost); acct->deferred_posts->erase(i); } } } xact_t * other = (*result.first).second; // Copy the two lists of postings (which should be relatively // short), and make sure that the intersection is the empty set // (i.e., that they are the same list). std::vector<post_t *> this_posts(xact->posts.begin(), xact->posts.end()); std::sort(this_posts.begin(), this_posts.end(), lt_posting_account); std::vector<post_t *> other_posts(other->posts.begin(), other->posts.end()); std::sort(other_posts.begin(), other_posts.end(), lt_posting_account); bool match = std::equal(this_posts.begin(), this_posts.end(), other_posts.begin(), is_equivalent_posting); if (! match || this_posts.size() != other_posts.size()) { add_error_context(_("While comparing this previously seen transaction:")); add_error_context(source_context(other->pos->pathname, other->pos->beg_pos, other->pos->end_pos, "> ")); add_error_context(_("to this later transaction:")); add_error_context(source_context(xact->pos->pathname, xact->pos->beg_pos, xact->pos->end_pos, "> ")); throw_(std::runtime_error, _f("Transactions with the same UUID must have equivalent postings")); } xact->journal = NULL; return false; } } xacts.push_back(xact); return true; } void journal_t::extend_xact(xact_base_t * xact) { foreach (auto_xact_t * auto_xact, auto_xacts) auto_xact->extend_xact(*xact, *current_context); } bool journal_t::remove_xact(xact_t * xact) { bool found = false; xacts_list::iterator i; for (i = xacts.begin(); i != xacts.end(); i++) if (*i == xact) { found = true; break; } if (! found) return false; xacts.erase(i); xact->journal = NULL; return true; } std::size_t journal_t::read(parse_context_stack_t& context) { std::size_t count = 0; try { parse_context_t& current(context.get_current()); current_context = ¤t; current.count = 0; if (! current.scope) current.scope = scope_t::default_scope; if (! current.scope) throw_(std::runtime_error, _f("No default scope in which to read journal file '%1%'") % current.pathname); if (! current.master) current.master = master; count = read_textual(context); if (count > 0) { if (! current.pathname.empty()) sources.push_back(fileinfo_t(current.pathname)); else sources.push_back(fileinfo_t()); } } catch (...) { clear_xdata(); current_context = NULL; throw; } // xdata may have been set for some accounts and transaction due to the use // of balance assertions or other calculations performed in valexpr-based // posting amounts. clear_xdata(); return count; } bool journal_t::has_xdata() { foreach (xact_t * xact, xacts) if (xact->has_xdata()) return true; foreach (auto_xact_t * xact, auto_xacts) if (xact->has_xdata()) return true; foreach (period_xact_t * xact, period_xacts) if (xact->has_xdata()) return true; if (master->has_xdata() || master->children_with_xdata()) return true; return false; } void journal_t::clear_xdata() { foreach (xact_t * xact, xacts) if (! xact->has_flags(ITEM_TEMP)) xact->clear_xdata(); foreach (auto_xact_t * xact, auto_xacts) if (! xact->has_flags(ITEM_TEMP)) xact->clear_xdata(); foreach (period_xact_t * xact, period_xacts) if (! xact->has_flags(ITEM_TEMP)) xact->clear_xdata(); master->clear_xdata(); } bool journal_t::valid() const { if (! master->valid()) { DEBUG("ledger.validate", "journal_t: master not valid"); return false; } foreach (const xact_t * xact, xacts) if (! xact->valid()) { DEBUG("ledger.validate", "journal_t: xact not valid"); return false; } return true; } } // namespace ledger ����������ledger-3.3.2/src/journal.h��������������������������������������������������������������������������0000664�0000000�0000000�00000014571�14411236400�0015376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file journal.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_JOURNAL_H #define INCLUDED_JOURNAL_H #include "utils.h" #include "times.h" #include "mask.h" #include "expr.h" namespace ledger { class xact_base_t; class xact_t; class auto_xact_t; class period_xact_t; class post_t; class account_t; class parse_context_t; class parse_context_stack_t; typedef std::list<xact_t *> xacts_list; typedef std::list<auto_xact_t *> auto_xacts_list; typedef std::list<period_xact_t *> period_xacts_list; typedef std::pair<mask_t, string> payee_alias_mapping_t; typedef std::list<payee_alias_mapping_t> payee_alias_mappings_t; typedef std::pair<string, string> payee_uuid_mapping_t; typedef std::list<payee_uuid_mapping_t> payee_uuid_mappings_t; typedef std::pair<mask_t, account_t *> account_mapping_t; typedef std::list<account_mapping_t> account_mappings_t; typedef std::map<string, account_t *> accounts_map; typedef std::map<string, xact_t *> checksum_map_t; typedef std::multimap<string, expr_t::check_expr_pair> tag_check_exprs_map; class journal_t : public noncopyable { public: struct fileinfo_t { optional<path> filename; datetime_t modtime; bool from_stream; fileinfo_t() : from_stream(true) { TRACE_CTOR(journal_t::fileinfo_t, ""); } fileinfo_t(const path& _filename) : filename(_filename), from_stream(false) { modtime = posix_time::from_time_t(last_write_time(*filename)); TRACE_CTOR(journal_t::fileinfo_t, "const path&"); } fileinfo_t(const fileinfo_t& info) : filename(info.filename), modtime(info.modtime), from_stream(info.from_stream) { TRACE_CTOR(journal_t::fileinfo_t, "copy"); } ~fileinfo_t() throw() { TRACE_DTOR(journal_t::fileinfo_t); } }; account_t * master; account_t * bucket; xacts_list xacts; auto_xacts_list auto_xacts; period_xacts_list period_xacts; std::list<fileinfo_t> sources; std::set<string> known_payees; std::set<string> known_tags; bool was_loaded; bool check_payees; bool day_break; bool recursive_aliases; bool no_aliases; payee_alias_mappings_t payee_alias_mappings; payee_uuid_mappings_t payee_uuid_mappings; account_mappings_t account_mappings; accounts_map account_aliases; account_mappings_t payees_for_unknown_accounts; checksum_map_t checksum_map; tag_check_exprs_map tag_check_exprs; optional<expr_t> value_expr; parse_context_t * current_context; enum checking_style_t { CHECK_PERMISSIVE, CHECK_NORMAL, CHECK_WARNING, CHECK_ERROR } checking_style; journal_t(); #if 0 journal_t(const path& pathname); journal_t(const string& str); #endif ~journal_t(); void initialize(); std::list<fileinfo_t>::iterator sources_begin() { return sources.begin(); } std::list<fileinfo_t>::iterator sources_end() { return sources.end(); } void add_account(account_t * acct); bool remove_account(account_t * acct); account_t * find_account(const string& name, bool auto_create = true); account_t * find_account_re(const string& regexp); account_t * expand_aliases(string name); account_t * register_account(const string& name, post_t * post, account_t * master = NULL); string register_payee(const string& name); string validate_payee(const string& name_or_alias); void register_commodity(commodity_t& comm, variant<int, xact_t *, post_t *> context); void register_metadata(const string& key, const value_t& value, variant<int, xact_t *, post_t *> context); bool add_xact(xact_t * xact); void extend_xact(xact_base_t * xact); bool remove_xact(xact_t * xact); xacts_list::iterator xacts_begin() { return xacts.begin(); } xacts_list::iterator xacts_end() { return xacts.end(); } auto_xacts_list::iterator auto_xacts_begin() { return auto_xacts.begin(); } auto_xacts_list::iterator auto_xacts_end() { return auto_xacts.end(); } period_xacts_list::iterator period_xacts_begin() { return period_xacts.begin(); } period_xacts_list::iterator period_xacts_end() { return period_xacts.end(); } std::size_t read(parse_context_stack_t& context); bool has_xdata(); void clear_xdata(); bool valid() const; private: std::size_t read_textual(parse_context_stack_t& context); bool should_check_payees(); bool payee_not_registered(const string& name); string translate_payee_name(const string& name); }; } // namespace ledger #endif // INCLUDED_JOURNAL_H ���������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/lookup.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000023473�14411236400�0015374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "lookup.h" #include "unistring.h" namespace ledger { namespace { typedef std::pair<xact_t *, int> score_entry_t; typedef std::deque<score_entry_t> scorecard_t; typedef std::map<uint32_t, std::size_t> char_positions_map; struct score_sorter { bool operator()(const score_entry_t& left, const score_entry_t& right) const { return left.second > right.second; } }; typedef std::map<account_t *, int> account_use_map; typedef std::pair<account_t *, int> account_use_pair; struct usage_sorter { bool operator()(const account_use_pair& left, const account_use_pair& right) const { return left.second < right.second; } }; } std::pair<xact_t *, account_t *> lookup_probable_account(const string& ident, xacts_list::reverse_iterator iter, xacts_list::reverse_iterator end, account_t * ref_account) { scorecard_t scores; #if !HAVE_BOOST_REGEX_UNICODE string lident = ident; to_lower(lident); unistring lowered_ident(lident); #else // jww (2010-03-07): Not yet implemented unistring lowered_ident(ident); #endif DEBUG("lookup.account", "Looking up identifier '" << lowered_ident.extract() << "'"); #if DEBUG_ON if (ref_account != NULL) DEBUG("lookup.account", " with reference account: " << ref_account->fullname()); #endif xact_t * xact; while (iter != end && (xact = *iter++) != NULL) { #if 0 // Only consider transactions from the last two years (jww (2010-03-07): // make this an option) if ((CURRENT_DATE() - xact->date()).days() > 700) continue; #endif // An exact match is worth a score of 100 and terminates the search if (ident == xact->payee) { DEBUG("lookup", " we have an exact match, score = 100"); scores.push_back(score_entry_t(xact, 100)); break; } #if !HAVE_BOOST_REGEX_UNICODE string payee = xact->payee; to_lower(payee); unistring value_key(payee); #else // jww (2010-03-07): Not yet implemented unistring value_key(xact->payee); #endif DEBUG("lookup", "Considering payee: " << value_key.extract()); std::size_t index = 0; std::size_t last_match_pos = unistring::npos; int bonus = 0; int score = 0; std::size_t pos; char_positions_map positions; // Walk each letter in the source identifier foreach (const uint32_t& ch, lowered_ident.utf32chars) { int addend = 0; bool added_bonus = false; std::size_t value_len = value_key.length(); pos = value_key.find(ch); // Ensure that a letter which has been matched is not matched twice, so // that the two x's of Exxon don't both match to the single x in Oxford. // This part of the loop is very expensive, but avoids a lot of bogus // matches. char_positions_map::iterator pi = positions.find(ch); while (pi != positions.end() && pos != unistring::npos && pos <= (*pi).second && (*pi).second + 1 < value_len) pos = value_key.find(ch, (*pi).second + 1); if (pos != unistring::npos) { if (pi != positions.end()) (*pi).second = pos; else positions.insert(char_positions_map::value_type(ch, pos)); // If it occurs in the same order as the source identifier -- that is, // without intervening letters to break the pattern -- it's worth 10 // points. Plus, an extra point is added for every letter in chains // of 3 or more. if (last_match_pos == unistring::npos ? index == 0 && pos == 0 : pos == last_match_pos + 1) { DEBUG("lookup", " char " << index << " in-sequence match with bonus " << bonus); addend += 10; if (bonus > 2) addend += bonus - 2; bonus++; added_bonus = true; last_match_pos = pos; } // If it occurs in the same general sequence as the source identifier, // it's worth 5 points, plus an extra point if it's within the next 3 // characters, and an extra point if it's preceded by a non-alphabetic // character. // // If the letter occurs at all in the target identifier, it's worth 1 // point, plus an extra point if it's within 3 characters, and an // extra point if it's preceded by a non-alphabetic character. else { bool in_order_match = (last_match_pos != unistring::npos && pos > last_match_pos); DEBUG("lookup", " char " << index << " " << (in_order_match ? "in-order" : "out-of-order") << " match" << (in_order_match && pos - index < 3 ? " with proximity bonus of 1" : "")); if (pos < index) addend += 1; else addend += 5; if (in_order_match && pos - index < 3) addend++; #if 0 #if !HAVE_BOOST_REGEX_UNICODE if (pos == 0 || (pos > 0 && !std::isalnum(value_key[pos - 1]))) addend++; #else // jww (2010-03-07): Not yet implemented #endif #endif last_match_pos = pos; } // If the letter does not appear at all, decrease the score by 1 } else { last_match_pos = unistring::npos; DEBUG("lookup", " char " << index << " does not match"); addend--; } // Finally, decay what is to be added to the score based on its position // in the word. Since credit card payees in particular often share // information at the end (such as the location where the purchase was // made), we want to give much more credence to what occurs at the // beginning. Every 5 character positions from the beginning becomes a // divisor for the addend. if ((int(index / 5) + 1) > 1) { DEBUG("lookup", " discounting the addend by / " << (int(index / 5) + 1)); addend = int(double(addend) / (int(index / 5) + 1)); } DEBUG("lookup", " final addend is " << addend); score += addend; DEBUG("lookup", " score is " << score); if (! added_bonus) bonus = 0; index++; } // Only consider payees with a score of 30 or greater if (score >= 30) scores.push_back(score_entry_t(xact, score)); } // Sort the results by descending score, then look at every account ever // used among the top five. Rank these by number of times used. Lastly, // "decay" any latter accounts, so that we give recently used accounts a // slightly higher rating in case of a tie. std::stable_sort(scores.begin(), scores.end(), score_sorter()); scorecard_t::iterator si = scores.begin(); int decay = 0; xact_t * best_xact = si != scores.end() ? (*si).first : NULL; account_use_map account_usage; for (int i = 0; i < 5 && si != scores.end(); i++, si++) { DEBUG("lookup.account", "Payee: " << std::setw(5) << std::right << (*si).second << " - " << (*si).first->payee); foreach (post_t * post, (*si).first->posts) { if (! post->has_flags(ITEM_TEMP | ITEM_GENERATED) && post->account != ref_account && ! post->account->has_flags(ACCOUNT_TEMP | ACCOUNT_GENERATED)) { account_use_map::iterator x = account_usage.find(post->account); if (x == account_usage.end()) account_usage.insert(account_use_pair(post->account, ((*si).second - decay))); else (*x).second += ((*si).second - decay); } decay++; } } if (account_usage.size() > 0) { #if DEBUG_ON if (SHOW_DEBUG("lookup.account")) { foreach (const account_use_pair& value, account_usage) { DEBUG("lookup.account", "Account: " << value.second << " - " << value.first->fullname()); } } #endif return std::pair<xact_t *, account_t *> (best_xact, (*std::max_element(account_usage.begin(), account_usage.end(), usage_sorter())).first); } else { return std::pair<xact_t *, account_t *>(best_xact, NULL); } } } // namespace ledger �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/lookup.h���������������������������������������������������������������������������0000664�0000000�0000000�00000004041�14411236400�0015224�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file lookup.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_LOOKUP_H #define INCLUDED_LOOKUP_H #include "iterators.h" namespace ledger { std::pair<xact_t *, account_t *> lookup_probable_account(const string& ident, xacts_list::reverse_iterator iter, xacts_list::reverse_iterator end, account_t * ref_account = NULL); } // namespace ledger #endif // INCLUDED_LOOKUP_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/main.cc����������������������������������������������������������������������������0000664�0000000�0000000�00000015764�14411236400�0015013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "global.h" // This is where the meat of main() is, which // was moved there for the sake of clarity here #include "session.h" #if HAVE_BOOST_NOWIDE #include <boost/nowide/args.hpp> #endif #ifdef HAVE_EDIT #include <editline/readline.h> #endif using namespace ledger; #if HAVE_BOOST_PYTHON namespace ledger { extern char * argv0; } #endif int main(int argc, char * argv[], char * envp[]) { int status = 1; #if HAVE_BOOST_NOWIDE boost::nowide::args a(argc, argv); // Fix command-line encoding on Windows #endif #if HAVE_BOOST_PYTHON argv0 = argv[0]; #endif // The very first thing we do is handle some very special command-line // options, since they affect how the environment is setup: // // --verify ; turns on memory tracing // --verbose ; turns on logging // --debug CATEGORY ; turns on debug logging // --trace LEVEL ; turns on trace logging // --memory ; turns on memory usage tracing // --init-file ; directs ledger to use a different init file handle_debug_options(argc, argv); #if VERIFY_ON IF_VERIFY() initialize_memory_tracing(); #endif INFO("Ledger starting"); // Initialize global Boost/C++ environment std::ios::sync_with_stdio(false); std::signal(SIGINT, sigint_handler); #if !defined(_WIN32) && !defined(__CYGWIN__) std::signal(SIGPIPE, sigpipe_handler); #endif #if HAVE_GETTEXT ::textdomain("ledger"); #endif global_scope_t * global_scope = NULL; try { // Create the session object, which maintains nearly all state relating to // this invocation of Ledger; and register all known journal parsers. global_scope = new global_scope_t(envp); global_scope->session().set_flush_on_next_data_file(true); // Construct an STL-style argument list from the process command arguments strings_list args; for (int i = 1; i < argc; i++) args.push_back(argv[i]); // Look for options and a command verb in the command-line arguments bind_scope_t bound_scope(*global_scope, global_scope->report()); args = global_scope->read_command_arguments(bound_scope, args); if (global_scope->HANDLED(script_)) { // Ledger is being invoked as a script command interpreter global_scope->session().read_journal_files(); status = 0; ifstream in(global_scope->HANDLER(script_).str()); while (status == 0 && ! in.eof()) { char line[1024]; in.getline(line, 1023); char * p = skip_ws(line); if (*p && *p != '#') status = global_scope->execute_command_wrapper(split_arguments(p), true); } } else if (! args.empty()) { // User has invoke a verb at the interactive command-line status = global_scope->execute_command_wrapper(args, false); } else { // Commence the REPL by displaying the current Ledger version global_scope->show_version_info(std::cout); global_scope->session().read_journal_files(); bool exit_loop = false; #ifdef HAVE_EDIT rl_readline_name = const_cast<char *>("Ledger"); // TODO: rl_attempted_completion_function = ledger_completion; while (char * p = readline(global_scope->prompt_string())) { char * expansion = NULL; int result; result = history_expand(skip_ws(p), &expansion); if (result < 0 || result == 2) { if (expansion) std::free(expansion); std::free(p); throw_(std::logic_error, _f("Failed to expand history reference '%1%'") % p); } else if (expansion) { add_history(expansion); } #else // HAVE_EDIT while (! std::cin.eof()) { std::cout << global_scope->prompt_string(); char line[1024]; std::cin.getline(line, 1023); char * p = skip_ws(line); #endif // HAVE_EDIT check_for_signal(); if (*p && *p != '#') { if (std::strncmp(p, "quit", 4) == 0) exit_loop = true; else global_scope->execute_command_wrapper(split_arguments(p), true); } #ifdef HAVE_EDIT if (expansion) std::free(expansion); std::free(p); #endif if (exit_loop) break; } status = 0; // report success } } catch (const std::exception& err) { if (global_scope) global_scope->report_error(err); else std::cerr << "Exception during initialization: " << err.what() << std::endl; } catch (const error_count& errors) { // used for a "quick" exit, and is used only if help text (such as // --help) was displayed status = static_cast<int>(errors.count); } // If memory verification is being performed (which can be very slow), clean // up everything by closing the session and deleting the session object, and // then shutting down the memory tracing subsystem. Otherwise, let it all // leak because we're about to exit anyway. #if VERIFY_ON IF_VERIFY() { checked_delete(global_scope); INFO("Ledger ended (Boost/libstdc++ may still hold memory)"); #if VERIFY_ON shutdown_memory_tracing(); #endif } else #endif { if (global_scope) global_scope->quick_close(); INFO("Ledger ended"); // let global_scope leak! } // Return the final status to the operating system, either 1 for error or 0 // for a successful completion. return status; } // main.cc ends here. ������������ledger-3.3.2/src/mask.cc����������������������������������������������������������������������������0000664�0000000�0000000�00000005156�14411236400�0015014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "mask.h" namespace ledger { mask_t::mask_t(const string& pat) : expr() { *this = pat; TRACE_CTOR(mask_t, "const string&"); } mask_t& mask_t::operator=(const string& pat) { #if HAVE_BOOST_REGEX_UNICODE expr = boost::make_u32regex(pat.c_str(), boost::regex::perl | boost::regex::icase); #else expr.assign(pat.c_str(), boost::regex::perl | boost::regex::icase); #endif VERIFY(valid()); return *this; } mask_t& mask_t::assign_glob(const string& pat) { string re_pat = ""; string::size_type len = pat.length(); for (string::size_type i = 0; i < len; i++) { switch (pat[i]) { case '?': re_pat += '.'; break; case '*': re_pat += ".*"; break; case '[': while (i < len && pat[i] != ']') re_pat += pat[i++]; if (i < len) re_pat += pat[i]; break; case '\\': if (i + 1 < len) { re_pat += pat[++i]; break; } // fallthrough... default: re_pat += pat[i]; break; } } return (*this = re_pat); } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/mask.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000007425�14411236400�0014657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup util */ /** * @file mask.h * @author John Wiegley * * @ingroup util * * @brief Regular expression masking. */ #ifndef INCLUDED_MASK_H #define INCLUDED_MASK_H #include "utils.h" #if HAVE_BOOST_REGEX_UNICODE #include "unistring.h" #endif namespace ledger { class mask_t { public: #if HAVE_BOOST_REGEX_UNICODE boost::u32regex expr; #else boost::regex expr; #endif explicit mask_t(const string& pattern); mask_t() : expr() { TRACE_CTOR(mask_t, ""); } mask_t(const mask_t& m) : expr(m.expr) { TRACE_CTOR(mask_t, "copy"); } ~mask_t() throw() { TRACE_DTOR(mask_t); } mask_t& operator=(const string& other); mask_t& assign_glob(const string& other); bool operator<(const mask_t& other) const { return expr < other.expr; } bool operator==(const mask_t& other) const { return expr == other.expr; } bool match(const string& text) const { #if HAVE_BOOST_REGEX_UNICODE DEBUG("mask.match", "Matching: \"" << text << "\" =~ /" << str() << "/ = " << (boost::u32regex_search(text, expr) ? "true" : "false")); return boost::u32regex_search(text, expr); #else DEBUG("mask.match", "Matching: \"" << text << "\" =~ /" << str() << "/ = " << (boost::regex_search(text, expr) ? "true" : "false")); return boost::regex_search(text, expr); #endif } bool empty() const { return expr.empty(); } string str() const { if (! empty()) { #if HAVE_BOOST_REGEX_UNICODE assert(sizeof(boost::uint32_t) == sizeof(UChar32)); unistring ustr; std::basic_string<UChar32> expr_str = expr.str(); std::copy(expr_str.begin(), expr_str.end(), std::back_inserter(ustr.utf32chars)); return ustr.extract(); #else return expr.str(); #endif } else { return empty_string; } } bool valid() const { if (expr.status() != 0) { DEBUG("ledger.validate", "mask_t: expr.status() != 0"); return false; } return true; } }; inline std::ostream& operator<<(std::ostream& out, const mask_t& mask) { out << mask.str(); return out; } inline void put_mask(property_tree::ptree& pt, const mask_t& mask) { pt.put_value(mask.str()); } } // namespace ledger #endif // INCLUDED_MASK_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/op.cc������������������������������������������������������������������������������0000664�0000000�0000000�00000064164�14411236400�0014503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "op.h" #include "scope.h" #include "commodity.h" #include "pool.h" namespace ledger { void intrusive_ptr_add_ref(const expr_t::op_t * op) { op->acquire(); } void intrusive_ptr_release(const expr_t::op_t * op) { op->release(); } value_t split_cons_expr(expr_t::ptr_op_t op) { if (op->kind == expr_t::op_t::O_CONS) { value_t seq; seq.push_back(expr_value(op->left())); expr_t::ptr_op_t next = op->right(); while (next) { expr_t::ptr_op_t value_op; if (next->kind == expr_t::op_t::O_CONS) { value_op = next->left(); next = next->has_right() ? next->right() : NULL; } else { value_op = next; next = NULL; } seq.push_back(expr_value(value_op)); } return seq; } else { return expr_value(op); } } namespace { inline void check_type_context(scope_t& scope, value_t& result) { if (scope.type_required() && scope.type_context() != value_t::VOID && result.type() != scope.type_context()) { throw_(calc_error, _f("Expected return of %1%, but received %2%") % result.label(scope.type_context()) % result.label()); } } } expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, scope_t * param_scope) { scope_t * scope_ptr = &scope; unique_ptr<scope_t> bound_scope; expr_t::ptr_op_t result; #if DEBUG_ON if (SHOW_DEBUG("expr.compile")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; DEBUG("expr.compile", ""); } #endif assert(kind < LAST); if (is_ident()) { DEBUG("expr.compile", "Lookup: " << as_ident() << " in " << scope_ptr); ptr_op_t def; if (param_scope) def = param_scope->lookup(symbol_t::FUNCTION, as_ident()); if (! def) def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident()); if (def) { // Identifier references are first looked up at the point of // definition, and then at the point of every use if they could // not be found there. #if DEBUG_ON if (SHOW_DEBUG("expr.compile")) { DEBUG("expr.compile", "Found definition:"); def->dump(*_log_stream, 0); } #endif // DEBUG_ON result = copy(def); } else if (left()) { result = copy(); } else { result = this; } } else if (is_scope()) { shared_ptr<scope_t> subscope(new symbol_scope_t(*scope_t::empty_scope)); set_scope(subscope); bound_scope.reset(new bind_scope_t(*scope_ptr, *subscope.get())); scope_ptr = bound_scope.get(); } else if (kind < TERMINALS) { result = this; } else if (kind == O_DEFINE) { switch (left()->kind) { case IDENT: { ptr_op_t node(right()->compile(*scope_ptr, depth + 1, param_scope)); DEBUG("expr.compile", "Defining " << left()->as_ident() << " in " << scope_ptr); scope_ptr->define(symbol_t::FUNCTION, left()->as_ident(), node); break; } case O_CALL: if (left()->left()->is_ident()) { ptr_op_t node(new op_t(op_t::O_LAMBDA)); node->set_left(left()->right()); node->set_right(right()); node = node->compile(*scope_ptr, depth + 1, param_scope); DEBUG("expr.compile", "Defining " << left()->left()->as_ident() << " in " << scope_ptr); scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(), node); break; } // fall through... default: throw_(compile_error, _("Invalid function definition")); } result = wrap_value(NULL_VALUE); } else if (kind == O_LAMBDA) { symbol_scope_t params(param_scope ? *param_scope : *scope_t::empty_scope); for (ptr_op_t sym = left(); sym; sym = sym->has_right() ? sym->right() : NULL) { ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym; if (! varname->is_ident()) { std::ostringstream buf; varname->dump(buf, 0); throw_(calc_error, _f("Invalid function or lambda parameter: %1%") % buf.str()); } else { DEBUG("expr.compile", "Defining function parameter " << varname->as_ident()); params.define(symbol_t::FUNCTION, varname->as_ident(), new op_t(PLUG)); } } ptr_op_t rhs(right()->compile(*scope_ptr, depth + 1, ¶ms)); if (rhs == right()) result = this; else result = copy(left(), rhs); } if (! result) { if (! left()) throw_(calc_error, _("Syntax error")); ptr_op_t lhs(left()->compile(*scope_ptr, depth + 1, param_scope)); ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ? (kind == O_LOOKUP ? right() : right()->compile(*scope_ptr, depth + 1, param_scope)) : NULL); if (lhs == left() && (! rhs || rhs == right())) { result = this; } else { ptr_op_t intermediate(copy(lhs, rhs)); // Reduce constants immediately if possible if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value())) result = wrap_value(intermediate->calc(*scope_ptr, NULL, depth + 1)); else result = intermediate; } } #if DEBUG_ON if (SHOW_DEBUG("expr.compile")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; DEBUG("expr.compile", ""); } #endif return result; } namespace { expr_t::ptr_op_t lookup_ident(expr_t::ptr_op_t op, scope_t& scope) { expr_t::ptr_op_t def = op->left(); // If no definition was pre-compiled for this identifier, look it up // in the current scope. if (! def || def->kind == expr_t::op_t::PLUG) { DEBUG("scope.symbols", "Looking for IDENT '" << op->as_ident() << "'"); def = scope.lookup(symbol_t::FUNCTION, op->as_ident()); } if (! def) throw_(calc_error, _f("Unknown identifier '%1%'") % op->as_ident()); return def; } } value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) { try { value_t result; #if DEBUG_ON if (SHOW_DEBUG("expr.calc")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; ledger::_log_buffer << op_context(this) << " => ..."; DEBUG("expr.calc", ""); } #endif switch (kind) { case VALUE: result = as_value(); break; case O_DEFINE: result = NULL_VALUE; break; case IDENT: if (ptr_op_t definition = lookup_ident(this, scope)) { // Evaluating an identifier is the same as calling its definition // directly result = definition->calc(scope, locus, depth + 1); check_type_context(scope, result); } break; case FUNCTION: { // Evaluating a FUNCTION is the same as calling it directly; this // happens when certain functions-that-look-like-variables (such as // "amount") are resolved. call_scope_t call_args(scope, locus, depth + 1); result = as_function()(call_args); check_type_context(scope, result); break; } case SCOPE: assert(! is_scope_unset()); if (is_scope_unset()) { symbol_scope_t subscope(scope); result = left()->calc(subscope, locus, depth + 1); } else { bind_scope_t bound_scope(scope, *as_scope()); result = left()->calc(bound_scope, locus, depth + 1); } break; case O_LOOKUP: { context_scope_t context_scope(scope, value_t::SCOPE); bool scope_error = true; if (value_t obj = left()->calc(context_scope, locus, depth + 1)) { if (obj.is_scope() && obj.as_scope() != NULL) { bind_scope_t bound_scope(scope, *obj.as_scope()); result = right()->calc(bound_scope, locus, depth + 1); scope_error = false; } } if (scope_error) throw_(calc_error, _("Left operand does not evaluate to an object")); break; } case O_CALL: result = calc_call(scope, locus, depth); check_type_context(scope, result); break; case O_LAMBDA: result = expr_value(this); break; case O_MATCH: result = (right()->calc(scope, locus, depth + 1).as_mask() .match(left()->calc(scope, locus, depth + 1).to_string())); break; case O_EQ: result = (left()->calc(scope, locus, depth + 1) == right()->calc(scope, locus, depth + 1)); break; case O_LT: result = (left()->calc(scope, locus, depth + 1) < right()->calc(scope, locus, depth + 1)); break; case O_LTE: result = (left()->calc(scope, locus, depth + 1) <= right()->calc(scope, locus, depth + 1)); break; case O_GT: result = (left()->calc(scope, locus, depth + 1) > right()->calc(scope, locus, depth + 1)); break; case O_GTE: result = (left()->calc(scope, locus, depth + 1) >= right()->calc(scope, locus, depth + 1)); break; case O_ADD: result = (left()->calc(scope, locus, depth + 1) + right()->calc(scope, locus, depth + 1)); break; case O_SUB: result = (left()->calc(scope, locus, depth + 1) - right()->calc(scope, locus, depth + 1)); break; case O_MUL: result = (left()->calc(scope, locus, depth + 1) * right()->calc(scope, locus, depth + 1)); break; case O_DIV: result = (left()->calc(scope, locus, depth + 1) / right()->calc(scope, locus, depth + 1)); break; case O_NEG: result = left()->calc(scope, locus, depth + 1).negated(); break; case O_NOT: result = ! left()->calc(scope, locus, depth + 1); break; case O_AND: if (left()->calc(scope, locus, depth + 1)) result = right()->calc(scope, locus, depth + 1); else result = false; break; case O_OR: if (value_t temp = left()->calc(scope, locus, depth + 1)) result = temp; else result = right()->calc(scope, locus, depth + 1); break; case O_QUERY: assert(right()); assert(right()->kind == O_COLON); if (value_t temp = left()->calc(scope, locus, depth + 1)) result = right()->left()->calc(scope, locus, depth + 1); else result = right()->right()->calc(scope, locus, depth + 1); break; case O_COLON: assert("We should never calculate an O_COLON operator" == NULL); break; case O_CONS: result = calc_cons(scope, locus, depth); break; case O_SEQ: result = calc_seq(scope, locus, depth); break; default: throw_(calc_error, _f("Unexpected expr node '%1%'") % op_context(this)); } #if DEBUG_ON if (SHOW_DEBUG("expr.calc")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; ledger::_log_buffer << op_context(this) << " => "; result.dump(ledger::_log_buffer, true); DEBUG("expr.calc", ""); } #endif return result; } catch (const std::exception&) { if (locus && ! *locus) *locus = this; throw; } } namespace { expr_t::ptr_op_t find_definition(expr_t::ptr_op_t op, scope_t& scope, expr_t::ptr_op_t * locus, const int depth, int recursion_depth = 0) { // If the object we are apply call notation to is a FUNCTION value // or an O_LAMBDA expression, then this is the object we want to // call. if (op->is_function() || op->kind == expr_t::op_t::O_LAMBDA) return op; if (recursion_depth > 256) throw_(value_error, _("Function recursion_depth too deep (> 256)")); // If it's an identifier, look up its definition and see if it's a // function. if (op->is_ident()) return find_definition(lookup_ident(op, scope), scope, locus, depth, recursion_depth + 1); // Value objects might be callable if they contain an expression. if (op->is_value()) { value_t def(op->as_value()); if (is_expr(def)) return find_definition(as_expr(def), scope, locus, depth, recursion_depth + 1); else throw_(value_error, _f("Cannot call %1% as a function") % def.label()); } // Resolve ordinary expressions. return find_definition(expr_t::op_t::wrap_value(op->calc(scope, locus, depth + 1)), scope, locus, depth + 1, recursion_depth + 1); } value_t call_lambda(expr_t::ptr_op_t func, scope_t& scope, call_scope_t& call_args, expr_t::ptr_op_t * locus, const int depth) { std::size_t args_index(0); std::size_t args_count(call_args.size()); symbol_scope_t args_scope(*scope_t::empty_scope); for (expr_t::ptr_op_t sym = func->left(); sym; sym = sym->has_right() ? sym->right() : NULL) { expr_t::ptr_op_t varname = sym->kind == expr_t::op_t::O_CONS ? sym->left() : sym; if (! varname->is_ident()) { throw_(calc_error, _("Invalid function definition")); } else if (args_index == args_count) { DEBUG("expr.calc", "Defining function argument as null: " << varname->as_ident()); args_scope.define(symbol_t::FUNCTION, varname->as_ident(), expr_t::op_t::wrap_value(NULL_VALUE)); } else { DEBUG("expr.calc", "Defining function argument from call_args: " << varname->as_ident()); args_scope.define(symbol_t::FUNCTION, varname->as_ident(), expr_t::op_t::wrap_value(call_args[args_index++])); } } if (args_index < args_count) throw_(calc_error, _f("Too few arguments in function call (saw %1%, wanted %2%)") % args_count % args_index); if (func->right()->is_scope()) { bind_scope_t outer_scope(scope, *func->right()->as_scope()); bind_scope_t bound_scope(outer_scope, args_scope); return func->right()->left()->calc(bound_scope, locus, depth + 1); } else { return func->right()->calc(args_scope, locus, depth + 1); } } } value_t expr_t::op_t::call(const value_t& args, scope_t& scope, ptr_op_t * locus, const int depth) { call_scope_t call_args(scope, locus, depth + 1); call_args.set_args(args); if (is_function()) return as_function()(call_args); else if (kind == O_LAMBDA) return call_lambda(this, scope, call_args, locus, depth); else return find_definition(this, scope, locus, depth) ->calc(call_args, locus, depth); } value_t expr_t::op_t::calc_call(scope_t& scope, ptr_op_t * locus, const int depth) { ptr_op_t func = left(); string name = func->is_ident() ? func->as_ident() : "<value expr>"; func = find_definition(func, scope, locus, depth); call_scope_t call_args(scope, locus, depth + 1); if (has_right()) call_args.set_args(split_cons_expr(right())); try { if (func->is_function()) { return func->as_function()(call_args); } else { assert(func->kind == O_LAMBDA); return call_lambda(func, scope, call_args, locus, depth); } } catch (const std::exception&) { add_error_context(_f("While calling function '%1% %2%':") % name % call_args.args); throw; } } value_t expr_t::op_t::calc_cons(scope_t& scope, ptr_op_t * locus, const int depth) { value_t result = left()->calc(scope, locus, depth + 1); if (has_right()) { value_t temp; temp.push_back(result); ptr_op_t next = right(); while (next) { ptr_op_t value_op; if (next->kind == O_CONS) { value_op = next->left(); next = next->has_right() ? next->right() : NULL; } else { value_op = next; next = NULL; } temp.push_back(value_op->calc(scope, locus, depth + 1)); } result = temp; } return result; } value_t expr_t::op_t::calc_seq(scope_t& scope, ptr_op_t * locus, const int depth) { // An O_SEQ is very similar to an O_CONS except that only the last // result value in the series is kept. O_CONS builds up a list. // // Another feature of O_SEQ is that it pushes a new symbol scope onto // the stack. We evaluate the left side here to catch any // side-effects, such as definitions in the case of 'x = 1; x'. value_t result = left()->calc(scope, locus, depth + 1); if (has_right()) { ptr_op_t next = right(); while (next) { ptr_op_t value_op; if (next->kind == O_SEQ) { value_op = next->left(); next = next->right(); } else { value_op = next; next = NULL; } result = value_op->calc(scope, locus, depth + 1); } } return result; } namespace { bool print_cons(std::ostream& out, const expr_t::const_ptr_op_t op, const expr_t::op_t::context_t& context) { bool found = false; assert(op->left()); if (op->left()->print(out, context)) found = true; if (op->has_right()) { out << ", "; if (op->right()->kind == expr_t::op_t::O_CONS) found = print_cons(out, op->right(), context); else if (op->right()->print(out, context)) found = true; } return found; } bool print_seq(std::ostream& out, const expr_t::const_ptr_op_t op, const expr_t::op_t::context_t& context) { bool found = false; assert(op->left()); if (op->left()->print(out, context)) found = true; if (op->has_right()) { out << "; "; if (op->right()->kind == expr_t::op_t::O_SEQ) found = print_seq(out, op->right(), context); else if (op->right()->print(out, context)) found = true; } return found; } } bool expr_t::op_t::print(std::ostream& out, const context_t& context) const { bool found = false; if (context.start_pos && this == context.op_to_find) { *context.start_pos = out.tellp(); *context.start_pos -= 1; found = true; } string symbol; if (kind > TERMINALS && (kind != O_CALL && kind != O_DEFINE)) out << '('; switch (kind) { case VALUE: as_value().dump(out, context.relaxed); break; case IDENT: out << as_ident(); break; case FUNCTION: out << "<FUNCTION>"; break; case SCOPE: if (left() && left()->print(out, context)) found = true; break; case O_NOT: out << "! "; if (left() && left()->print(out, context)) found = true; break; case O_NEG: out << "- "; if (left() && left()->print(out, context)) found = true; break; case O_ADD: if (left() && left()->print(out, context)) found = true; out << " + "; if (has_right() && right()->print(out, context)) found = true; break; case O_SUB: if (left() && left()->print(out, context)) found = true; out << " - "; if (has_right() && right()->print(out, context)) found = true; break; case O_MUL: if (left() && left()->print(out, context)) found = true; out << " * "; if (has_right() && right()->print(out, context)) found = true; break; case O_DIV: if (left() && left()->print(out, context)) found = true; out << " / "; if (has_right() && right()->print(out, context)) found = true; break; case O_EQ: if (left() && left()->print(out, context)) found = true; out << " == "; if (has_right() && right()->print(out, context)) found = true; break; case O_LT: if (left() && left()->print(out, context)) found = true; out << " < "; if (has_right() && right()->print(out, context)) found = true; break; case O_LTE: if (left() && left()->print(out, context)) found = true; out << " <= "; if (has_right() && right()->print(out, context)) found = true; break; case O_GT: if (left() && left()->print(out, context)) found = true; out << " > "; if (has_right() && right()->print(out, context)) found = true; break; case O_GTE: if (left() && left()->print(out, context)) found = true; out << " >= "; if (has_right() && right()->print(out, context)) found = true; break; case O_AND: if (left() && left()->print(out, context)) found = true; out << " & "; if (has_right() && right()->print(out, context)) found = true; break; case O_OR: if (left() && left()->print(out, context)) found = true; out << " | "; if (has_right() && right()->print(out, context)) found = true; break; case O_QUERY: if (left() && left()->print(out, context)) found = true; out << " ? "; if (has_right() && right()->print(out, context)) found = true; break; case O_COLON: if (left() && left()->print(out, context)) found = true; out << " : "; if (has_right() && right()->print(out, context)) found = true; break; case O_CONS: found = print_cons(out, this, context); break; case O_SEQ: found = print_seq(out, this, context); break; case O_DEFINE: if (left() && left()->print(out, context)) found = true; out << " = "; if (has_right() && right()->print(out, context)) found = true; break; case O_LOOKUP: if (left() && left()->print(out, context)) found = true; out << "."; if (has_right() && right()->print(out, context)) found = true; break; case O_LAMBDA: if (left() && left()->print(out, context)) found = true; out << " -> "; if (has_right() && right()->print(out, context)) found = true; break; case O_CALL: if (left() && left()->print(out, context)) found = true; if (has_right()) { if (right()->kind == O_CONS) { if (right()->print(out, context)) found = true; } else { out << "("; if (has_right() && right()->print(out, context)) found = true; out << ")"; } } else { out << "()"; } break; case O_MATCH: if (left() && left()->print(out, context)) found = true; out << " =~ "; if (has_right() && right()->print(out, context)) found = true; break; case LAST: default: assert(false); break; } if (kind > TERMINALS && (kind != O_CALL && kind != O_DEFINE)) out << ')'; if (! symbol.empty()) { if (commodity_pool_t::current_pool->find(symbol)) out << '@'; out << symbol; } if (context.end_pos && this == context.op_to_find) { *context.end_pos = out.tellp(); *context.end_pos -= 1; } return found; } void expr_t::op_t::dump(std::ostream& out, const int depth) const { out.setf(std::ios::left); out.width((sizeof(void *) * 2) + 2); out << this; for (int i = 0; i < depth; i++) out << " "; switch (kind) { case PLUG: out << "PLUG"; break; case VALUE: out << "VALUE: "; as_value().dump(out); break; case IDENT: out << "IDENT: " << as_ident(); break; case FUNCTION: out << "FUNCTION"; break; case SCOPE: out << "SCOPE: "; if (is_scope_unset()) out << "null"; else out << as_scope().get(); break; case O_DEFINE: out << "O_DEFINE"; break; case O_LOOKUP: out << "O_LOOKUP"; break; case O_LAMBDA: out << "O_LAMBDA"; break; case O_CALL: out << "O_CALL"; break; case O_MATCH: out << "O_MATCH"; break; case O_NOT: out << "O_NOT"; break; case O_NEG: out << "O_NEG"; break; case O_ADD: out << "O_ADD"; break; case O_SUB: out << "O_SUB"; break; case O_MUL: out << "O_MUL"; break; case O_DIV: out << "O_DIV"; break; case O_EQ: out << "O_EQ"; break; case O_LT: out << "O_LT"; break; case O_LTE: out << "O_LTE"; break; case O_GT: out << "O_GT"; break; case O_GTE: out << "O_GTE"; break; case O_AND: out << "O_AND"; break; case O_OR: out << "O_OR"; break; case O_QUERY: out << "O_QUERY"; break; case O_COLON: out << "O_COLON"; break; case O_CONS: out << "O_CONS"; break; case O_SEQ: out << "O_SEQ"; break; case LAST: default: assert(false); break; } out << " (" << refc << ')' << std::endl; // An identifier is a special non-terminal, in that its left() can // hold the compiled definition of the identifier. if (kind > TERMINALS || is_scope() || is_ident()) { if (left()) { left()->dump(out, depth + 1); if (kind > UNARY_OPERATORS && has_right()) right()->dump(out, depth + 1); } else if (kind > UNARY_OPERATORS) { assert(! has_right()); } } } string op_context(const expr_t::ptr_op_t op, const expr_t::ptr_op_t locus) { std::ostream::pos_type start_pos, end_pos; expr_t::op_t::context_t context(op, locus, &start_pos, &end_pos); std::ostringstream buf; buf << " "; if (op->print(buf, context)) { buf << "\n"; for (int i = 0; i <= end_pos; i++) { if (i > start_pos) buf << "^"; else buf << " "; } } return buf.str(); } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/op.h�������������������������������������������������������������������������������0000664�0000000�0000000�00000021443�14411236400�0014336�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file op.h * @author John Wiegley * * @ingroup expr */ #ifndef INCLUDED_OP_H #define INCLUDED_OP_H #include "expr.h" namespace ledger { class expr_t::op_t : public noncopyable { friend class expr_t; friend class expr_t::parser_t; public: typedef expr_t::ptr_op_t ptr_op_t; private: mutable short refc; ptr_op_t left_; variant<boost::blank, ptr_op_t, // used by all binary operators value_t, // used by constant VALUE string, // used by constant IDENT expr_t::func_t, // used by terminal FUNCTION shared_ptr<scope_t> // used by terminal SCOPE > data; public: enum kind_t { // Constants PLUG, VALUE, IDENT, CONSTANTS, FUNCTION, SCOPE, TERMINALS, // Binary operators O_NOT, O_NEG, UNARY_OPERATORS, O_EQ, O_LT, O_LTE, O_GT, O_GTE, O_AND, O_OR, O_ADD, O_SUB, O_MUL, O_DIV, O_QUERY, O_COLON, O_CONS, O_SEQ, O_DEFINE, O_LOOKUP, O_LAMBDA, O_CALL, O_MATCH, BINARY_OPERATORS, OPERATORS, UNKNOWN, LAST }; kind_t kind; explicit op_t() : refc(0), kind(UNKNOWN) { TRACE_CTOR(op_t, ""); } explicit op_t(const kind_t _kind) : refc(0), kind(_kind) { TRACE_CTOR(op_t, "const kind_t"); } ~op_t() { TRACE_DTOR(op_t); assert(refc == 0); } bool is_value() const { if (kind == VALUE) { assert(data.type() == typeid(value_t)); return true; } return false; } value_t& as_value_lval() { assert(is_value()); value_t& val(boost::get<value_t>(data)); VERIFY(val.valid()); return val; } const value_t& as_value() const { return const_cast<op_t *>(this)->as_value_lval(); } void set_value(const value_t& val) { VERIFY(val.valid()); data = val; } bool is_ident() const { if (kind == IDENT) { assert(data.type() == typeid(string)); return true; } return false; } string& as_ident_lval() { assert(is_ident()); return boost::get<string>(data); } const string& as_ident() const { return const_cast<op_t *>(this)->as_ident_lval(); } void set_ident(const string& val) { data = val; } bool is_function() const { return kind == FUNCTION; } expr_t::func_t& as_function_lval() { assert(is_function()); return boost::get<expr_t::func_t>(data); } const expr_t::func_t& as_function() const { return const_cast<op_t *>(this)->as_function_lval(); } void set_function(const expr_t::func_t& val) { data = val; } bool is_scope() const { return kind == SCOPE; } bool is_scope_unset() const { return data.which() == 0; } shared_ptr<scope_t> as_scope_lval() { assert(is_scope()); return boost::get<shared_ptr<scope_t> >(data); } const shared_ptr<scope_t> as_scope() const { return const_cast<op_t *>(this)->as_scope_lval(); } void set_scope(shared_ptr<scope_t> val) { data = val; } // These three functions must use 'kind == IDENT' rather than // 'is_ident()', because they are called before the `data' member gets // set, which is_ident() tests. ptr_op_t& left() { assert(kind > TERMINALS || kind == IDENT || is_scope()); return left_; } const ptr_op_t& left() const { assert(kind > TERMINALS || kind == IDENT || is_scope()); return left_; } void set_left(const ptr_op_t& expr) { assert(kind > TERMINALS || kind == IDENT || is_scope()); left_ = expr; } ptr_op_t& as_op_lval() { assert(kind > TERMINALS || is_ident()); return boost::get<ptr_op_t>(data); } const ptr_op_t& as_op() const { return const_cast<op_t *>(this)->as_op_lval(); } ptr_op_t& right() { assert(kind > TERMINALS); return as_op_lval(); } const ptr_op_t& right() const { assert(kind > TERMINALS); return as_op(); } void set_right(const ptr_op_t& expr) { assert(kind > TERMINALS); data = expr; } bool has_right() const { if (kind < TERMINALS) return false; return data.which() != 0 && as_op(); } private: void acquire() const { DEBUG("op.memory", "Acquiring " << this << ", refc now " << refc + 1); assert(refc >= 0); refc++; } void release() const { DEBUG("op.memory", "Releasing " << this << ", refc now " << refc - 1); assert(refc > 0); if (--refc == 0) checked_delete(this); } friend void intrusive_ptr_add_ref(const op_t * op); friend void intrusive_ptr_release(const op_t * op); ptr_op_t copy(ptr_op_t _left = NULL, ptr_op_t _right = NULL) const { ptr_op_t node(new_node(kind, _left, _right)); if (kind < TERMINALS) node->data = data; return node; } public: static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL, ptr_op_t _right = NULL); ptr_op_t compile(scope_t& scope, const int depth = 0, scope_t * param_scope = NULL); value_t calc(scope_t& scope, ptr_op_t * locus = NULL, const int depth = 0); value_t call(const value_t& args, scope_t& scope, ptr_op_t * locus = NULL, const int depth = 0); struct context_t { ptr_op_t expr_op; ptr_op_t op_to_find; std::ostream::pos_type * start_pos; std::ostream::pos_type * end_pos; bool relaxed; context_t() : start_pos(NULL), end_pos(NULL), relaxed(false) {} context_t(const ptr_op_t& _expr_op, const ptr_op_t& _op_to_find, std::ostream::pos_type * const _start_pos = NULL, std::ostream::pos_type * const _end_pos = NULL, const bool _relaxed = true) : expr_op(_expr_op), op_to_find(_op_to_find), start_pos(_start_pos), end_pos(_end_pos), relaxed(_relaxed) {} }; bool print(std::ostream& out, const context_t& context = context_t()) const; void dump(std::ostream& out, const int depth = 0) const; static ptr_op_t wrap_value(const value_t& val); static ptr_op_t wrap_functor(expr_t::func_t fobj); static ptr_op_t wrap_scope(shared_ptr<scope_t> sobj); private: value_t calc_call(scope_t& scope, ptr_op_t * locus, const int depth); value_t calc_cons(scope_t& scope, ptr_op_t * locus, const int depth); value_t calc_seq(scope_t& scope, ptr_op_t * locus, const int depth); }; inline expr_t::ptr_op_t expr_t::op_t::new_node(kind_t _kind, ptr_op_t _left, ptr_op_t _right) { ptr_op_t node(new op_t(_kind)); if (_left) node->set_left(_left); if (_right) node->set_right(_right); return node; } inline expr_t::ptr_op_t expr_t::op_t::wrap_value(const value_t& val) { ptr_op_t temp(new op_t(op_t::VALUE)); temp->set_value(val); return temp; } inline expr_t::ptr_op_t expr_t::op_t::wrap_functor(expr_t::func_t fobj) { ptr_op_t temp(new op_t(op_t::FUNCTION)); temp->set_function(fobj); return temp; } #define MAKE_FUNCTOR(x) expr_t::op_t::wrap_functor(bind(&x, this, _1)) #define WRAP_FUNCTOR(x) expr_t::op_t::wrap_functor(x) string op_context(const expr_t::ptr_op_t op, const expr_t::ptr_op_t locus = NULL); value_t split_cons_expr(expr_t::ptr_op_t op); } // namespace ledger #endif // INCLUDED_OP_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/option.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000016626�14411236400�0015375�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "option.h" namespace ledger { namespace { typedef std::pair<expr_t::ptr_op_t, bool> op_bool_tuple; op_bool_tuple find_option(scope_t& scope, const string& name) { char buf[128]; char * p = buf; if (name.length() > 127) { throw_(option_error, _f("Illegal option --%1%") % name); } foreach (char ch, name) { if (ch == '-') *p++ = '_'; else *p++ = ch; } *p++ = '_'; *p = '\0'; if (expr_t::ptr_op_t op = scope.lookup(symbol_t::OPTION, buf)) return op_bool_tuple(op, true); *--p = '\0'; return op_bool_tuple(scope.lookup(symbol_t::OPTION, buf), false); } op_bool_tuple find_option(scope_t& scope, const char letter) { char buf[4]; buf[0] = letter; buf[1] = '_'; buf[2] = '\0'; if (expr_t::ptr_op_t op = scope.lookup(symbol_t::OPTION, buf)) return op_bool_tuple(op, true); buf[1] = '\0'; return op_bool_tuple(scope.lookup(symbol_t::OPTION, buf), false); } void process_option(const string& whence, const expr_t::func_t& opt, scope_t& scope, const char * arg, const string& name) { try { call_scope_t args(scope); args.push_back(string_value(whence)); if (arg) args.push_back(string_value(arg)); opt(args); } catch (const std::exception&) { if (name[0] == '-') add_error_context(_f("While parsing option '%1%'") % name); else add_error_context(_f("While parsing environment variable '%1%'") % name); throw; } } } bool process_option(const string& whence, const string& name, scope_t& scope, const char * arg, const string& varname) { op_bool_tuple opt(find_option(scope, name)); if (opt.first) { process_option(whence, opt.first->as_function(), scope, arg, varname); return true; } return false; } void process_environment(const char ** envp, const string& tag, scope_t& scope) { const char * tag_p = tag.c_str(); string::size_type tag_len = tag.length(); assert(tag_p); assert(tag_len > 0); for (const char ** p = envp; *p; p++) { if (std::strlen(*p) >= tag_len && std::strncmp(*p, tag_p, tag_len) == 0) { char buf[8192]; char * r = buf; const char * q; for (q = *p + tag_len; *q && *q != '=' && r - buf < 8191; q++) if (*q == '_') *r++ = '-'; else *r++ = static_cast<char>(std::tolower(*q)); *r = '\0'; if (*q == '=') { try { string value = string(*p, static_cast<std::string::size_type>(q - *p)); if (! value.empty()) process_option(string("$") + buf, string(buf), scope, q + 1, value); } catch (const std::exception&) { add_error_context(_f("While parsing environment variable option '%1%':") % *p); throw; } } } } } namespace { struct op_bool_char_tuple { expr_t::ptr_op_t op; bool truth; char ch; op_bool_char_tuple(expr_t::ptr_op_t _op, bool _truth, char _ch) : op(_op), truth(_truth), ch(_ch) {} }; } strings_list process_arguments(strings_list args, scope_t& scope) { bool anywhere = true; strings_list remaining; for (strings_list::iterator i = args.begin(); i != args.end(); i++) { DEBUG("option.args", "Examining argument '" << *i << "'"); if (! anywhere || (*i)[0] != '-') { DEBUG("option.args", " adding to list of real args"); remaining.push_back(*i); continue; } // --long-option or -s if ((*i)[1] == '-') { if ((*i)[2] == '\0') { DEBUG("option.args", " it's a --, ending options processing"); anywhere = false; continue; } DEBUG("option.args", " it's an option string"); string opt_name; const char * name = (*i).c_str() + 2; const char * value = NULL; if (const char * p = std::strchr(name, '=')) { opt_name = string(name, static_cast<std::string::size_type>(p - name)); value = ++p; DEBUG("option.args", " read option value from option: " << value); } else { opt_name = name; } op_bool_tuple opt(find_option(scope, opt_name)); if (! opt.first) throw_(option_error, _f("Illegal option --%1%") % name); if (opt.second && ! value && ++i != args.end() && value == NULL) { value = (*i).c_str(); DEBUG("option.args", " read option value from arg: " << value); if (value == NULL) throw_(option_error, _f("Missing option argument for --%1%") % name); } process_option(string("--") + name, opt.first->as_function(), scope, value, string("--") + name); } else if ((*i)[1] == '\0') { throw_(option_error, _f("illegal option -%1%") % (*i)[0]); } else { DEBUG("option.args", " single-char option"); std::list<op_bool_char_tuple> option_queue; std::string::size_type x = 1; for (char c = (*i)[x]; c != '\0'; x++, c = (*i)[x]) { op_bool_tuple opt(find_option(scope, c)); if (! opt.first) throw_(option_error, _f("Illegal option -%1%") % c); option_queue.push_back(op_bool_char_tuple(opt.first, opt.second, c)); } foreach (op_bool_char_tuple& o, option_queue) { const char * value = NULL; if (o.truth && ++i != args.end()) { value = (*i).c_str(); DEBUG("option.args", " read option value from arg: " << value); if (value == NULL) throw_(option_error, _f("Missing option argument for -%1%") % o.ch); } process_option(string("-") + o.ch, o.op->as_function(), scope, value, string("-") + o.ch); } } } return remaining; } } // namespace ledger ����������������������������������������������������������������������������������������������������������ledger-3.3.2/src/option.h���������������������������������������������������������������������������0000664�0000000�0000000�00000022571�14411236400�0015233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file option.h * @author John Wiegley * * @ingroup expr */ #ifndef INCLUDED_OPTION_H #define INCLUDED_OPTION_H #include "scope.h" namespace ledger { class call_scope_t; template <typename T> class option_t { protected: const char * name; string::size_type name_len; const char ch; bool handled; optional<string> source; option_t& operator=(const option_t&); public: T * parent; string value; bool wants_arg; option_t(const char * _name, const char _ch = '\0') : name(_name), name_len(std::strlen(name)), ch(_ch), handled(false), parent(NULL), value(), wants_arg(name_len > 0 ? name[name_len - 1] == '_' : false) { DEBUG("option.names", "Option: " << name); TRACE_CTOR(option_t, "const char *, const char"); } option_t(const option_t& other) : name(other.name), name_len(other.name_len), ch(other.ch), handled(other.handled), parent(NULL), value(other.value), wants_arg(other.wants_arg) { TRACE_CTOR(option_t, "copy"); } virtual ~option_t() { TRACE_DTOR(option_t); } void report(std::ostream& out) const { if (handled && source) { out.width(24); out << std::right << desc(); if (wants_arg) { out << " = "; out.width(42); out << std::left << value; } else { out.width(45); out << ' '; } out << std::left << *source << std::endl; } } string desc() const { std::ostringstream out; out << "--"; for (const char * p = name; *p; p++) { if (*p == '_') { if (*(p + 1)) out << '-'; } else { out << *p; } } if (ch) out << " (-" << ch << ")"; return out.str(); } operator bool() const { return handled; } string str() const { assert(handled); if (value.empty()) throw_(std::runtime_error, _f("No argument provided for %1%") % desc()); return value; } void on(const char * whence) { on(string(whence)); } void on(const optional<string>& whence) { handler_thunk(whence); handled = true; source = whence; } void on(const char * whence, const string& str) { on(string(whence), str); } void on(const optional<string>& whence, const string& str) { string before = value; handler_thunk(whence, str); if (value == before) value = str; handled = true; source = whence; } void off() { handled = false; value = ""; source = none; } virtual void handler_thunk(const optional<string>&) {} virtual void handler_thunk(const optional<string>&, const string&) {} value_t handler(call_scope_t& args) { if (wants_arg) { if (args.size() < 2) throw_(std::runtime_error, _f("No argument provided for %1%") % desc()); else if (args.size() > 2) throw_(std::runtime_error, _f("To many arguments provided for %1%") % desc()); else if (! args[0].is_string()) throw_(std::runtime_error, _f("Context argument for %1% not a string") % desc()); on(args.get<string>(0), args.get<string>(1)); } else if (args.size() < 1) { throw_(std::runtime_error, _f("No argument provided for %1%") % desc()); } else if (! args[0].is_string()) { throw_(std::runtime_error, _f("Context argument for %1% not a string") % desc()); } else { on(args.get<string>(0)); } return true; } virtual value_t operator()(call_scope_t& args) { if (! args.empty()) { args.push_front(string_value("?expr")); return handler(args); } else if (wants_arg) { return string_value(value); } else { return handled; } } }; #define BEGIN(type, name) \ struct name ## option_t : public option_t<type> #define CTOR(type, name) \ name ## option_t() : option_t<type>(#name) #define CTOR_(type, name, base) \ name ## option_t() : option_t<type>(#name), base #define DECL1(type, name, vartype, var, value) \ vartype var ; \ name ## option_t() : option_t<type>(#name), var value #define DO() virtual void handler_thunk(const optional<string>& whence) #define DO_(var) virtual void handler_thunk(const optional<string>& whence, \ const string& var) #define END(name) name ## handler #define COPY_OPT(name, other) name ## handler(other.name ## handler) #define MAKE_OPT_HANDLER(type, x) \ expr_t::op_t::wrap_functor(bind(&option_t<type>::handler, x, _1)) #define MAKE_OPT_FUNCTOR(type, x) \ expr_t::op_t::wrap_functor(bind(&option_t<type>::operator(), x, _1)) inline bool is_eq(const char * p, const char * n) { // Test whether p matches n, substituting - in p for _ in n. for (; *p && *n; p++, n++) { if (! (*p == '-' && *n == '_') && *p != *n) return false; } // Ignore any trailing underscore return *p == *n || (! *p && *n == '_' && ! *(n + 1)); } #define OPT(name) \ if (is_eq(p, #name)) \ return ((name ## handler).parent = this, &(name ## handler)) #define OPT_ALT(name, alt) \ if (is_eq(p, #name) || is_eq(p, #alt)) \ return ((name ## handler).parent = this, &(name ## handler)) #define OPT_(name) \ if (! *(p + 1) || \ ((name ## handler).wants_arg && \ *(p + 1) == '_' && ! *(p + 2)) || \ is_eq(p, #name)) \ return ((name ## handler).parent = this, &(name ## handler)) #define OPT_CH(name) \ if (! *(p + 1) || \ ((name ## handler).wants_arg && \ *(p + 1) == '_' && ! *(p + 2))) \ return ((name ## handler).parent = this, &(name ## handler)) #define HANDLER(name) name ## handler #define HANDLED(name) HANDLER(name) #define OPTION(type, name) \ BEGIN(type, name) \ { \ CTOR(type, name) {} \ } \ END(name) #define OPTION_(type, name, body) \ BEGIN(type, name) \ { \ CTOR(type, name) {} \ body \ } \ END(name) #define OPTION__(type, name, body) \ BEGIN(type, name) \ { \ body \ } \ END(name) #define OTHER(name) \ parent->HANDLER(name).parent = parent; \ parent->HANDLER(name) bool process_option(const string& whence, const string& name, scope_t& scope, const char * arg, const string& varname); void process_environment(const char ** envp, const string& tag, scope_t& scope); strings_list process_arguments(strings_list args, scope_t& scope); DECLARE_EXCEPTION(option_error, std::runtime_error); } // namespace ledger #endif // INCLUDED_OPTION_H ���������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/output.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000030477�14411236400�0015425�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "output.h" #include "xact.h" #include "post.h" #include "item.h" #include "account.h" #include "session.h" #include "report.h" namespace ledger { format_posts::format_posts(report_t& _report, const string& format, const optional<string>& _prepend_format, std::size_t _prepend_width) : report(_report), prepend_width(_prepend_width), last_xact(NULL), last_post(NULL), first_report_title(true) { const char * f = format.c_str(); if (const char * p = std::strstr(f, "%/")) { first_line_format.parse_format (string(f, 0, static_cast<std::string::size_type>(p - f))); const char * n = p + 2; if (const char * pp = std::strstr(n, "%/")) { next_lines_format.parse_format (string(n, 0, static_cast<std::string::size_type>(pp - n)), first_line_format); between_format.parse_format(string(pp + 2), first_line_format); } else { next_lines_format.parse_format(string(n), first_line_format); } } else { first_line_format.parse_format(format); next_lines_format.parse_format(format); } if (_prepend_format) prepend_format.parse_format(*_prepend_format); TRACE_CTOR(format_posts, "report&, const string&, bool"); } void format_posts::flush() { report.output_stream.flush(); } void format_posts::operator()(post_t& post) { if (! post.has_xdata() || ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { std::ostream& out(report.output_stream); bind_scope_t bound_scope(report, post); if (! report_title.empty()) { if (first_report_title) first_report_title = false; else out << '\n'; value_scope_t val_scope(bound_scope, string_value(report_title)); format_t group_title_format(report.HANDLER(group_title_format_).str()); out << group_title_format(val_scope); report_title = ""; } if (prepend_format) { out.width(static_cast<std::streamsize>(prepend_width)); out << prepend_format(bound_scope); } if (last_xact != post.xact) { if (last_xact) { bind_scope_t xact_scope(report, *last_xact); out << between_format(xact_scope); } out << first_line_format(bound_scope); last_xact = post.xact; } else if (last_post && last_post->date() != post.date()) { out << first_line_format(bound_scope); } else { out << next_lines_format(bound_scope); } post.xdata().add_flags(POST_EXT_DISPLAYED); last_post = &post; } } format_accounts::format_accounts(report_t& _report, const string& format, const optional<string>& _prepend_format, std::size_t _prepend_width) : report(_report), prepend_width(_prepend_width), disp_pred(), first_report_title(true) { const char * f = format.c_str(); if (const char * p = std::strstr(f, "%/")) { account_line_format.parse_format (string(f, 0, static_cast<std::string::size_type>(p - f))); const char * n = p + 2; if (const char * pp = std::strstr(n, "%/")) { total_line_format.parse_format (string(n, 0, static_cast<std::string::size_type>(pp - n)), account_line_format); separator_format.parse_format(string(pp + 2), account_line_format); } else { total_line_format.parse_format(n, account_line_format); } } else { account_line_format.parse_format(format); total_line_format.parse_format(format, account_line_format); } if (_prepend_format) prepend_format.parse_format(*_prepend_format); TRACE_CTOR(format_accounts, "report&, const string&"); } std::size_t format_accounts::post_account(account_t& account, const bool flat) { if (! flat && account.parent) post_account(*account.parent, flat); if (account.xdata().has_flags(ACCOUNT_EXT_TO_DISPLAY) && ! account.xdata().has_flags(ACCOUNT_EXT_DISPLAYED)) { std::ostream& out(report.output_stream); DEBUG("account.display", "Displaying account: " << account.fullname()); account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED); bind_scope_t bound_scope(report, account); if (! report_title.empty()) { if (first_report_title) first_report_title = false; else out << '\n'; value_scope_t val_scope(bound_scope, string_value(report_title)); format_t group_title_format(report.HANDLER(group_title_format_).str()); out << group_title_format(val_scope); report_title = ""; } if (prepend_format) { out.width(static_cast<std::streamsize>(prepend_width)); out << prepend_format(bound_scope); } out << account_line_format(bound_scope); return 1; } return 0; } std::pair<std::size_t, std::size_t> format_accounts::mark_accounts(account_t& account, const bool flat) { std::size_t visited = 0; std::size_t to_display = 0; foreach (accounts_map::value_type& pair, account.accounts) { std::pair<std::size_t, std::size_t> i = mark_accounts(*pair.second, flat); visited += i.first; to_display += i.second; } #if DEBUG_ON DEBUG("account.display", "Considering account: " << account.fullname()); if (account.has_xflags(ACCOUNT_EXT_VISITED)) DEBUG("account.display", " it was visited itself"); DEBUG("account.display", " it has " << visited << " visited children"); DEBUG("account.display", " it has " << to_display << " children to display"); #endif if (account.parent && (account.has_xflags(ACCOUNT_EXT_VISITED) || (! flat && visited > 0))) { bind_scope_t bound_scope(report, account); call_scope_t call_scope(bound_scope); if ((! flat && to_display > 1) || ((flat || to_display != 1 || account.has_xflags(ACCOUNT_EXT_VISITED)) && (report.HANDLED(empty) || report.display_value(report.fn_display_total(call_scope))) && disp_pred(bound_scope))) { account.xdata().add_flags(ACCOUNT_EXT_TO_DISPLAY); DEBUG("account.display", "Marking account as TO_DISPLAY"); to_display = 1; } visited = 1; } return std::pair<std::size_t, std::size_t>(visited, to_display); } void format_accounts::flush() { std::ostream& out(report.output_stream); if (report.HANDLED(display_)) { DEBUG("account.display", "Account display predicate: " << report.HANDLER(display_).str()); disp_pred.parse(report.HANDLER(display_).str()); } mark_accounts(*report.session.journal->master, report.HANDLED(flat)); std::size_t displayed = 0; foreach (account_t * account, posted_accounts) displayed += post_account(*account, report.HANDLED(flat)); if (displayed > 1 && ! report.HANDLED(no_total) && ! report.HANDLED(percent)) { bind_scope_t bound_scope(report, *report.session.journal->master); out << separator_format(bound_scope); if (prepend_format) { static_cast<std::ostream&>(report.output_stream) .width(static_cast<std::streamsize>(prepend_width)); static_cast<std::ostream&>(report.output_stream) << prepend_format(bound_scope); } out << total_line_format(bound_scope); } out.flush(); } void format_accounts::operator()(account_t& account) { DEBUG("account.display", "Posting account: " << account.fullname()); posted_accounts.push_back(&account); } void report_accounts::flush() { std::ostream& out(report.output_stream); format_t prepend_format; std::size_t prepend_width; bool do_prepend_format; if ((do_prepend_format = report.HANDLED(prepend_format_))) { prepend_format.parse_format(report.HANDLER(prepend_format_).str()); prepend_width = report.HANDLED(prepend_width_) ? lexical_cast<std::size_t>(report.HANDLER(prepend_width_).str()) : 0; } foreach (accounts_pair& entry, accounts) { if (do_prepend_format) { bind_scope_t bound_scope(report, *entry.first); out.width(static_cast<std::streamsize>(prepend_width)); out << prepend_format(bound_scope); } if (report.HANDLED(count)) out << entry.second << ' '; out << *entry.first << '\n'; } } void report_accounts::operator()(post_t& post) { accounts_report_map::iterator i = accounts.find(post.account); if (i == accounts.end()) accounts.insert(accounts_pair(post.account, 1)); else (*i).second++; } void report_payees::flush() { std::ostream& out(report.output_stream); foreach (payees_pair& entry, payees) { if (report.HANDLED(count)) out << entry.second << ' '; out << entry.first << '\n'; } } void report_payees::operator()(post_t& post) { std::map<string, std::size_t>::iterator i = payees.find(post.payee()); if (i == payees.end()) payees.insert(payees_pair(post.payee(), 1)); else (*i).second++; } void report_tags::flush() { std::ostream& out(report.output_stream); foreach (tags_pair& entry, tags) { if (report.HANDLED(count)) out << entry.second << ' '; out << entry.first << '\n'; } } void report_tags::gather_metadata(item_t& item) { if (! item.metadata) return; foreach (const item_t::string_map::value_type& data, *item.metadata) { string tag(data.first); if (report.HANDLED(values) && data.second.first) tag += ": " + data.second.first.get().to_string(); std::map<string, std::size_t>::iterator i = tags.find(tag); if (i == tags.end()) tags.insert(tags_pair(tag, 1)); else (*i).second++; } } void report_tags::operator()(post_t& post) { gather_metadata(*post.xact); gather_metadata(post); } void report_commodities::flush() { std::ostream& out(report.output_stream); foreach (commodities_pair& entry, commodities) { if (report.HANDLED(count)) out << entry.second << ' '; out << *entry.first << '\n'; } } void report_commodities::operator()(post_t& post) { amount_t temp(post.amount.strip_annotations(report.what_to_keep())); commodity_t& comm(temp.commodity()); commodities_report_map::iterator i = commodities.find(&comm); if (i == commodities.end()) commodities.insert(commodities_pair(&comm, 1)); else (*i).second++; if (comm.has_annotation()) { annotated_commodity_t& ann_comm(as_annotated_commodity(comm)); if (ann_comm.details.price) { commodities_report_map::iterator ii = commodities.find(&ann_comm.details.price->commodity()); if (ii == commodities.end()) commodities.insert (commodities_pair(&ann_comm.details.price->commodity(), 1)); else (*ii).second++; } } if (post.cost) { amount_t temp_cost(post.cost->strip_annotations(report.what_to_keep())); i = commodities.find(&temp_cost.commodity()); if (i == commodities.end()) commodities.insert(commodities_pair(&temp_cost.commodity(), 1)); else (*i).second++; } } } // namespace ledger �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/output.h���������������������������������������������������������������������������0000664�0000000�0000000�00000014165�14411236400�0015263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup report */ /** * @file output.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_OUTPUT_H #define INCLUDED_OUTPUT_H #include "chain.h" #include "predicate.h" #include "format.h" #include "account.h" namespace ledger { class xact_t; class post_t; class item_t; class report_t; class format_posts : public item_handler<post_t> { protected: report_t& report; format_t first_line_format; format_t next_lines_format; format_t between_format; format_t prepend_format; std::size_t prepend_width; xact_t * last_xact; post_t * last_post; bool first_report_title; string report_title; public: format_posts(report_t& _report, const string& format, const optional<string>& _prepend_format = none, std::size_t _prepend_width = 0); virtual ~format_posts() { TRACE_DTOR(format_posts); } virtual void title(const string& str) { report_title = str; } virtual void flush(); virtual void operator()(post_t& post); virtual void clear() { last_xact = NULL; last_post = NULL; report_title = ""; item_handler<post_t>::clear(); } }; class format_accounts : public item_handler<account_t> { protected: report_t& report; format_t account_line_format; format_t total_line_format; format_t separator_format; format_t prepend_format; std::size_t prepend_width; predicate_t disp_pred; bool first_report_title; string report_title; std::list<account_t *> posted_accounts; public: format_accounts(report_t& _report, const string& _format, const optional<string>& _prepend_format = none, std::size_t _prepend_width = 0); virtual ~format_accounts() { TRACE_DTOR(format_accounts); } std::pair<std::size_t, std::size_t> mark_accounts(account_t& account, const bool flat); virtual void title(const string& str) { report_title = str; } virtual std::size_t post_account(account_t& account, const bool flat); virtual void flush(); virtual void operator()(account_t& account); virtual void clear() { disp_pred.mark_uncompiled(); posted_accounts.clear(); report_title = ""; item_handler<account_t>::clear(); } }; class report_accounts : public item_handler<post_t> { protected: report_t& report; typedef std::map<account_t *, std::size_t>::value_type accounts_pair; typedef std::map<account_t *, std::size_t, account_compare> accounts_report_map; accounts_report_map accounts; public: report_accounts(report_t& _report) : report(_report) { TRACE_CTOR(report_accounts, "report&"); } virtual ~report_accounts() { TRACE_DTOR(report_accounts); } virtual void flush(); virtual void operator()(post_t& post); virtual void clear() { accounts.clear(); item_handler<post_t>::clear(); } }; class report_payees : public item_handler<post_t> { protected: report_t& report; std::map<string, std::size_t> payees; typedef std::map<string, std::size_t>::value_type payees_pair; public: report_payees(report_t& _report) : report(_report) { TRACE_CTOR(report_payees, "report&"); } virtual ~report_payees() { TRACE_DTOR(report_payees); } virtual void flush(); virtual void operator()(post_t& post); virtual void clear() { payees.clear(); item_handler<post_t>::clear(); } }; class report_tags : public item_handler<post_t> { protected: report_t& report; std::map<string, std::size_t> tags; typedef std::map<string, std::size_t>::value_type tags_pair; public: report_tags(report_t& _report) : report(_report) { TRACE_CTOR(report_tags, "report&"); } virtual ~report_tags() { TRACE_DTOR(report_tags); } virtual void flush(); virtual void gather_metadata(item_t& item); virtual void operator()(post_t& post); virtual void clear() { tags.clear(); item_handler<post_t>::clear(); } }; class report_commodities : public item_handler<post_t> { protected: report_t& report; typedef std::map<commodity_t *, std::size_t>::value_type commodities_pair; typedef std::map<commodity_t *, std::size_t, commodity_compare> commodities_report_map; commodities_report_map commodities; public: report_commodities(report_t& _report) : report(_report) { TRACE_CTOR(report_commodities, "report&"); } virtual ~report_commodities() { TRACE_DTOR(report_commodities); } virtual void flush(); virtual void operator()(post_t& post); virtual void clear() { commodities.clear(); item_handler<post_t>::clear(); } }; } // namespace ledger #endif // INCLUDED_OUTPUT_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/parser.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000040106�14411236400�0015347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "parser.h" namespace ledger { expr_t::ptr_op_t expr_t::parser_t::parse_value_term(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node; token_t& tok = next_token(in, tflags); switch (tok.kind) { case token_t::VALUE: node = new op_t(op_t::VALUE); node->set_value(tok.value); break; case token_t::IDENT: { string ident = tok.value.as_string(); node = new op_t(op_t::IDENT); node->set_ident(ident); break; } case token_t::LPAREN: node = parse_value_expr(in, tflags.plus_flags(PARSE_PARTIAL) .minus_flags(PARSE_SINGLE)); tok = next_token(in, tflags, token_t::RPAREN); break; default: push_token(tok); break; } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_call_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_value_term(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::LPAREN) { ptr_op_t prev(node); node = new op_t(op_t::O_CALL); node->set_left(prev); push_token(tok); // let the parser see the '(' again node->set_right(parse_value_expr(in, tflags.plus_flags(PARSE_SINGLE))); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_dot_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_call_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::DOT) { ptr_op_t prev(node); node = new op_t(op_t::O_LOOKUP); node->set_left(prev); node->set_right(parse_call_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_unary_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node; token_t& tok = next_token(in, tflags); switch (tok.kind) { case token_t::EXCLAM: { ptr_op_t term(parse_dot_expr(in, tflags)); if (! term) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); // A very quick optimization if (term->kind == op_t::VALUE) { term->as_value_lval().in_place_not(); node = term; } else { node = new op_t(op_t::O_NOT); node->set_left(term); } break; } case token_t::MINUS: { ptr_op_t term(parse_dot_expr(in, tflags)); if (! term) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); // A very quick optimization if (term->kind == op_t::VALUE) { term->as_value_lval().in_place_negate(); node = term; } else { node = new op_t(op_t::O_NEG); node->set_left(term); } break; } default: push_token(tok); node = parse_dot_expr(in, tflags); break; } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_mul_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_unary_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::STAR || tok.kind == token_t::SLASH || tok.kind == token_t::KW_DIV) { ptr_op_t prev(node); node = new op_t(tok.kind == token_t::STAR ? op_t::O_MUL : op_t::O_DIV); node->set_left(prev); node->set_right(parse_unary_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_add_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_mul_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::PLUS || tok.kind == token_t::MINUS) { ptr_op_t prev(node); node = new op_t(tok.kind == token_t::PLUS ? op_t::O_ADD : op_t::O_SUB); node->set_left(prev); node->set_right(parse_mul_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_logic_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_add_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { op_t::kind_t kind = op_t::LAST; parse_flags_t _flags = tflags; token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); bool negate = false; switch (tok.kind) { case token_t::EQUAL: if (tflags.has_flags(PARSE_NO_ASSIGN)) tok.rewind(in); else kind = op_t::O_EQ; break; case token_t::NEQUAL: kind = op_t::O_EQ; negate = true; break; case token_t::MATCH: kind = op_t::O_MATCH; break; case token_t::NMATCH: kind = op_t::O_MATCH; negate = true; break; case token_t::LESS: kind = op_t::O_LT; break; case token_t::LESSEQ: kind = op_t::O_LTE; break; case token_t::GREATER: kind = op_t::O_GT; break; case token_t::GREATEREQ: kind = op_t::O_GTE; break; default: push_token(tok); goto exit_loop; } if (kind != op_t::LAST) { ptr_op_t prev(node); node = new op_t(kind); node->set_left(prev); node->set_right(parse_add_expr(in, _flags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); if (negate) { prev = node; node = new op_t(op_t::O_NOT); node->set_left(prev); } } } } exit_loop: return node; } expr_t::ptr_op_t expr_t::parser_t::parse_and_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_logic_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::KW_AND) { ptr_op_t prev(node); node = new op_t(op_t::O_AND); node->set_left(prev); node->set_right(parse_logic_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_or_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_and_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::KW_OR) { ptr_op_t prev(node); node = new op_t(op_t::O_OR); node->set_left(prev); node->set_right(parse_and_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_querycolon_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_or_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::QUERY) { ptr_op_t prev(node); node = new op_t(op_t::O_QUERY); node->set_left(prev); node->set_right(parse_or_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT), token_t::COLON); prev = node->right(); ptr_op_t subnode = new op_t(op_t::O_COLON); subnode->set_left(prev); subnode->set_right(parse_or_expr(in, tflags)); if (! subnode->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); node->set_right(subnode); } else if (tok.kind == token_t::KW_IF) { ptr_op_t if_op(parse_or_expr(in, tflags)); if (! if_op) throw_(parse_error, _("'if' keyword not followed by argument")); tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::KW_ELSE) { ptr_op_t else_op(parse_or_expr(in, tflags)); if (! else_op) throw_(parse_error, _("'else' keyword not followed by argument")); ptr_op_t subnode = new op_t(op_t::O_COLON); subnode->set_left(node); subnode->set_right(else_op); node = new op_t(op_t::O_QUERY); node->set_left(if_op); node->set_right(subnode); } else { ptr_op_t null_node = new op_t(op_t::VALUE); null_node->set_value(NULL_VALUE); ptr_op_t subnode = new op_t(op_t::O_COLON); subnode->set_left(node); subnode->set_right(null_node); node = new op_t(op_t::O_QUERY); node->set_left(if_op); node->set_right(subnode); push_token(tok); } } else { push_token(tok); } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_comma_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_querycolon_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { ptr_op_t next; while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::COMMA) { if (! next) { ptr_op_t prev(node); node = new op_t(op_t::O_CONS); node->set_left(prev); next = node; } token_t& ntok = next_token(in, tflags); push_token(ntok); if (ntok.kind == token_t::RPAREN) break; ptr_op_t chain(new op_t(op_t::O_CONS)); chain->set_left(parse_querycolon_expr(in, tflags)); next->set_right(chain); next = chain; } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_lambda_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_comma_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::ARROW) { ptr_op_t prev(node); node = new op_t(op_t::O_LAMBDA); node->set_left(prev); ptr_op_t scope(new op_t(op_t::SCOPE)); scope->set_left(parse_querycolon_expr(in, tflags)); node->set_right(scope); } else { push_token(tok); } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_assign_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_lambda_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::ASSIGN) { ptr_op_t prev(node); node = new op_t(op_t::O_DEFINE); node->set_left(prev); ptr_op_t scope(new op_t(op_t::SCOPE)); scope->set_left(parse_lambda_expr(in, tflags)); node->set_right(scope); } else { push_token(tok); } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_value_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_assign_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { ptr_op_t chain; while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::SEMI) { ptr_op_t seq(new op_t(op_t::O_SEQ)); if (! chain) { seq->set_left(node); node = seq; } else { seq->set_left(chain->right()); chain->set_right(seq); } seq->set_right(parse_assign_expr(in, tflags)); chain = seq; } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse(std::istream& in, const parse_flags_t& flags, const optional<string>& original_string) { try { ptr_op_t top_node = parse_value_expr(in, flags); if (use_lookahead) { use_lookahead = false; lookahead.rewind(in); } lookahead.clear(); return top_node; } catch (const std::exception&) { if (original_string) { add_error_context(_("While parsing value expression:")); std::streamoff end_pos = 0; if (in.good()) end_pos = in.tellg(); std::streamoff pos = end_pos; if (pos > 0) pos -= lookahead.length; DEBUG("parser.error", "original_string = '" << *original_string << "'"); DEBUG("parser.error", " pos = " << pos); DEBUG("parser.error", " end_pos = " << end_pos); DEBUG("parser.error", " token kind = " << int(lookahead.kind)); DEBUG("parser.error", " token length = " << lookahead.length); add_error_context(line_context(*original_string, static_cast<string::size_type>(pos), static_cast<string::size_type>(end_pos))); } throw; } } } // namespace ledger ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/parser.h���������������������������������������������������������������������������0000664�0000000�0000000�00000010533�14411236400�0015212�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file parser.h * @author John Wiegley * * @ingroup expr */ #ifndef INCLUDED_PARSER_H #define INCLUDED_PARSER_H #include "token.h" #include "op.h" namespace ledger { class expr_t::parser_t : public noncopyable { mutable token_t lookahead; mutable bool use_lookahead; token_t& next_token(std::istream& in, const parse_flags_t& tflags, const optional<token_t::kind_t>& expecting = none) const { if (use_lookahead) use_lookahead = false; else lookahead.next(in, tflags); if (expecting && lookahead.kind != *expecting) lookahead.expected(*expecting); return lookahead; } #if !NO_ASSERTS void push_token(const token_t& tok) const { assert(&tok == &lookahead); use_lookahead = true; } #else void push_token(const token_t&) const { use_lookahead = true; } #endif // !NO_ASSERTS void push_token() const { use_lookahead = true; } ptr_op_t parse_value_term(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_call_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_dot_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_unary_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_mul_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_add_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_logic_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_and_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_or_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_querycolon_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_comma_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_lambda_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_assign_expr(std::istream& in, const parse_flags_t& flags) const; ptr_op_t parse_value_expr(std::istream& in, const parse_flags_t& flags) const; public: parser_t() : use_lookahead(false) { TRACE_CTOR(parser_t, ""); } ~parser_t() throw() { TRACE_DTOR(parser_t); } ptr_op_t parse(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT, const optional<string>& original_string = boost::none); }; } // namespace ledger #endif // INCLUDED_PARSER_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/pool.cc����������������������������������������������������������������������������0000664�0000000�0000000�00000031150�14411236400�0015023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "amount.h" #include "commodity.h" #include "annotate.h" #include "pool.h" #include "history.h" #include "quotes.h" namespace ledger { shared_ptr<commodity_pool_t> commodity_pool_t::current_pool; commodity_pool_t::commodity_pool_t() : default_commodity(NULL), keep_base(false), quote_leeway(86400), get_quotes(false), get_commodity_quote(commodity_quote_from_script) { null_commodity = create(""); null_commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); TRACE_CTOR(commodity_pool_t, ""); } commodity_t * commodity_pool_t::create(const string& symbol) { shared_ptr<commodity_t::base_t> base_commodity(new commodity_t::base_t(symbol)); shared_ptr<commodity_t> commodity(new commodity_t(this, base_commodity)); DEBUG("pool.commodities", "Creating base commodity " << symbol); // Create the "qualified symbol" version of this commodity's symbol if (commodity_t::symbol_needs_quotes(symbol)) { commodity->qualified_symbol = "\""; *commodity->qualified_symbol += symbol; *commodity->qualified_symbol += "\""; } DEBUG("pool.commodities", "Creating commodity '" << symbol << "'"); #if DEBUG_ON std::pair<commodities_map::iterator, bool> result = #endif commodities.insert(commodities_map::value_type(symbol, commodity)); #if DEBUG_ON assert(result.second); #endif commodity_price_history.add_commodity(*commodity.get()); return commodity.get(); } commodity_t * commodity_pool_t::find(const string& symbol) { DEBUG("pool.commodities", "Find commodity " << symbol); commodities_map::const_iterator i = commodities.find(symbol); if (i != commodities.end()) return (*i).second.get(); return NULL; } commodity_t * commodity_pool_t::find_or_create(const string& symbol) { DEBUG("pool.commodities", "Find-or-create commodity " << symbol); if (commodity_t * commodity = find(symbol)) return commodity; return create(symbol); } commodity_t * commodity_pool_t::alias(const string& name, commodity_t& referent) { commodities_map::const_iterator i = commodities.find(referent.base_symbol()); assert(i != commodities.end()); std::pair<commodities_map::iterator, bool> result = commodities.insert(commodities_map::value_type(name, (*i).second)); assert(result.second); return (*result.first).second.get(); } commodity_t * commodity_pool_t::create(const string& symbol, const annotation_t& details) { DEBUG("pool.commodities", "commodity_pool_t::create[ann] " << "symbol " << symbol << std::endl << details); if (details) return create(*find_or_create(symbol), details); else return create(symbol); } commodity_t * commodity_pool_t::find(const string& symbol, const annotation_t& details) { DEBUG("pool.commodities", "commodity_pool_t::find[ann] " << "symbol " << symbol << std::endl << details); annotated_commodities_map::const_iterator i = annotated_commodities.find (annotated_commodities_map::key_type(symbol, details)); if (i != annotated_commodities.end()) { DEBUG("pool.commodities", "commodity_pool_t::find[ann] found " << "symbol " << (*i).second->base_symbol() << std::endl << as_annotated_commodity(*(*i).second.get()).details); return (*i).second.get(); } else { return NULL; } } commodity_t * commodity_pool_t::find_or_create(const string& symbol, const annotation_t& details) { DEBUG("pool.commodities", "commodity_pool_t::find_or_create[ann] " << "symbol " << symbol << std::endl << details); if (details) { if (commodity_t * ann_comm = find(symbol, details)) { assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); return ann_comm; } else { return create(symbol, details); } } else { return find_or_create(symbol); } } commodity_t * commodity_pool_t::find_or_create(commodity_t& comm, const annotation_t& details) { DEBUG("pool.commodities", "commodity_pool_t::find_or_create[ann:comm] " << "symbol " << comm.base_symbol() << std::endl << details); if (details) { if (commodity_t * ann_comm = find(comm.base_symbol(), details)) { assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); return ann_comm; } else { return create(comm, details); } } else { return &comm; } } annotated_commodity_t * commodity_pool_t::create(commodity_t& comm, const annotation_t& details) { DEBUG("pool.commodities", "commodity_pool_t::create[ann:comm] " << "symbol " << comm.base_symbol() << std::endl << details); assert(comm); assert(! comm.has_annotation()); assert(details); shared_ptr<annotated_commodity_t> commodity(new annotated_commodity_t(&comm, details)); comm.add_flags(COMMODITY_SAW_ANNOTATED); if (details.price) { if (details.has_flags(ANNOTATION_PRICE_FIXATED)) comm.add_flags(COMMODITY_SAW_ANN_PRICE_FIXATED); else comm.add_flags(COMMODITY_SAW_ANN_PRICE_FLOAT); } DEBUG("pool.commodities", "Creating annotated commodity " << "symbol " << commodity->base_symbol() << std::endl << details); #if DEBUG_ON std::pair<annotated_commodities_map::iterator, bool> result = #endif annotated_commodities.insert(annotated_commodities_map::value_type (annotated_commodities_map::key_type (comm.base_symbol(), details), commodity)); #if DEBUG_ON assert(result.second); #endif return commodity.get(); } void commodity_pool_t::exchange(commodity_t& commodity, const amount_t& per_unit_cost, const datetime_t& moment) { DEBUG("commodity.prices.add", "exchanging commodity " << commodity << " at per unit cost " << per_unit_cost << " on " << moment); commodity_t& base_commodity (commodity.annotated ? as_annotated_commodity(commodity).referent() : commodity); base_commodity.add_price(moment, per_unit_cost); } cost_breakdown_t commodity_pool_t::exchange(const amount_t& amount, const amount_t& cost, const bool is_per_unit, const bool add_price, const optional<datetime_t>& moment, const optional<string>& tag) { DEBUG("commodity.prices.add", "exchange: " << amount << " for " << cost); DEBUG("commodity.prices.add", "exchange: is-per-unit = " << is_per_unit); #if DEBUG_ON if (moment) DEBUG("commodity.prices.add", "exchange: moment = " << *moment); if (tag) DEBUG("commodity.prices.add", "exchange: tag = " << *tag); #endif commodity_t& commodity(amount.commodity()); annotation_t * current_annotation = NULL; if (commodity.annotated) current_annotation = &as_annotated_commodity(commodity).details; amount_t per_unit_cost = (is_per_unit || amount.is_zero()) ? cost.abs() : (cost / amount).abs(); if (! cost.has_commodity()) per_unit_cost.clear_commodity(); if (cost.has_annotation()) per_unit_cost = per_unit_cost.strip_annotations(keep_details_t()); DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost); // Do not record commodity exchanges where amount's commodity has a // fixated price, since this does not establish a market value for the // base commodity. if (add_price && ! per_unit_cost.is_realzero() && (current_annotation == NULL || ! (current_annotation->price && current_annotation->has_flags(ANNOTATION_PRICE_FIXATED))) && commodity.referent() != per_unit_cost.commodity().referent()) { exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME()); } cost_breakdown_t breakdown; breakdown.final_cost = ! is_per_unit ? cost : cost * amount.abs(); DEBUG("commodity.prices.add", "exchange: final-cost = " << breakdown.final_cost); if (current_annotation && current_annotation->price) breakdown.basis_cost = (*current_annotation->price * amount).unrounded(); else breakdown.basis_cost = breakdown.final_cost; DEBUG("commodity.prices.add", "exchange: basis-cost = " << breakdown.basis_cost); annotation_t annotation(per_unit_cost, moment ? moment->date() : optional<date_t>(), tag); annotation.add_flags(ANNOTATION_PRICE_CALCULATED); if (current_annotation && current_annotation->has_flags(ANNOTATION_PRICE_FIXATED)) annotation.add_flags(ANNOTATION_PRICE_FIXATED); if (moment) annotation.add_flags(ANNOTATION_DATE_CALCULATED); if (tag) annotation.add_flags(ANNOTATION_TAG_CALCULATED); breakdown.amount = amount_t(amount, annotation); DEBUG("commodity.prices.add", "exchange: amount = " << breakdown.amount); return breakdown; } optional<std::pair<commodity_t *, price_point_t> > commodity_pool_t::parse_price_directive (char * line, bool do_not_add_price, bool no_date) { char * date_field_ptr = line; char * time_field_ptr = next_element(date_field_ptr); if (! time_field_ptr) return none; string date_field = date_field_ptr; char * symbol_and_price; datetime_t datetime; string symbol; if (! no_date && std::isdigit(time_field_ptr[0])) { symbol_and_price = next_element(time_field_ptr); if (! symbol_and_price) return none; datetime = parse_datetime(date_field + " " + time_field_ptr); } else if (! no_date && std::isdigit(date_field_ptr[0])) { symbol_and_price = time_field_ptr; datetime = datetime_t(parse_date(date_field)); } else { symbol = date_field_ptr; symbol_and_price = time_field_ptr; datetime = CURRENT_TIME(); } if (symbol.empty()) commodity_t::parse_symbol(symbol_and_price, symbol); price_point_t point; point.when = datetime; point.price.parse(symbol_and_price, PARSE_NO_MIGRATE); VERIFY(point.price.valid()); DEBUG("commodity.download", "Looking up symbol: " << symbol); if (commodity_t * commodity = find_or_create(symbol)) { DEBUG("commodity.download", "Adding price for " << symbol << ": " << point.when << " " << point.price); if (! do_not_add_price) commodity->add_price(point.when, point.price, true); commodity->add_flags(COMMODITY_KNOWN); return std::pair<commodity_t *, price_point_t>(commodity, point); } return none; } commodity_t * commodity_pool_t::parse_price_expression(const std::string& str, const bool add_prices, const optional<datetime_t>& moment) { scoped_array<char> buf(new char[str.length() + 1]); std::strcpy(buf.get(), str.c_str()); char * price = std::strchr(buf.get(), '='); if (price) *price++ = '\0'; if (commodity_t * commodity = find_or_create(trim_ws(buf.get()))) { if (price && add_prices) { for (char * p = std::strtok(price, ";"); p; p = std::strtok(NULL, ";")) { commodity->add_price(moment ? *moment : CURRENT_TIME(), amount_t(p)); } } return commodity; } return NULL; } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/pool.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000011674�14411236400�0014676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup math */ /** * @file pool.h * @author John Wiegley * * @ingroup math * * @brief Types for managing commodity pools * * Long. */ #ifndef INCLUDED_POOL_H #define INCLUDED_POOL_H #include "history.h" #include "annotate.h" namespace ledger { struct cost_breakdown_t { amount_t amount; amount_t final_cost; amount_t basis_cost; }; class commodity_pool_t : public noncopyable { public: /** * The commodities collection in commodity_pool_t maintains pointers to all * the commodities which have ever been created by the user, whether * explicitly by calling the create methods of commodity_pool_t, or * implicitly by parsing a commoditized amount. */ typedef std::map<string, shared_ptr<commodity_t> > commodities_map; typedef std::map<std::pair<string, annotation_t>, shared_ptr<annotated_commodity_t> > annotated_commodities_map; commodities_map commodities; annotated_commodities_map annotated_commodities; commodity_history_t commodity_price_history; commodity_t * null_commodity; commodity_t * default_commodity; bool keep_base; // --base optional<path> price_db; // --price-db= long quote_leeway; // --leeway= bool get_quotes; // --download function<optional<price_point_t> (commodity_t& commodity, const commodity_t * in_terms_of)> get_commodity_quote; static shared_ptr<commodity_pool_t> current_pool; explicit commodity_pool_t(); virtual ~commodity_pool_t() { TRACE_DTOR(commodity_pool_t); } commodity_t * create(const string& symbol); commodity_t * find(const string& name); commodity_t * find_or_create(const string& symbol); commodity_t * alias(const string& name, commodity_t& referent); commodity_t * create(const string& symbol, const annotation_t& details); commodity_t * find(const string& symbol, const annotation_t& details); commodity_t * find_or_create(const string& symbol, const annotation_t& details); commodity_t * find_or_create(commodity_t& comm, const annotation_t& details); annotated_commodity_t * create(commodity_t& comm, const annotation_t& details); // Exchange one commodity for another, while recording the factored price. void exchange(commodity_t& commodity, const amount_t& per_unit_cost, const datetime_t& moment); cost_breakdown_t exchange(const amount_t& amount, const amount_t& cost, const bool is_per_unit = false, const bool add_price = true, const optional<datetime_t>& moment = none, const optional<string>& tag = none); // Parse commodity prices from a textual representation optional<std::pair<commodity_t *, price_point_t> > parse_price_directive(char * line, bool do_not_add_price = false, bool no_date = false); commodity_t * parse_price_expression(const std::string& str, const bool add_prices = true, const optional<datetime_t>& moment = none); }; } // namespace ledger #endif // INCLUDED_POOL_H ��������������������������������������������������������������������ledger-3.3.2/src/post.cc����������������������������������������������������������������������������0000664�0000000�0000000�00000050550�14411236400�0015044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "post.h" #include "xact.h" #include "account.h" #include "journal.h" #include "format.h" #include "pool.h" namespace ledger { bool post_t::has_tag(const string& tag, bool inherit) const { if (item_t::has_tag(tag)) return true; if (inherit && xact) return xact->has_tag(tag); return false; } bool post_t::has_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask, bool inherit) const { if (item_t::has_tag(tag_mask, value_mask)) return true; if (inherit && xact) return xact->has_tag(tag_mask, value_mask); return false; } optional<value_t> post_t::get_tag(const string& tag, bool inherit) const { if (optional<value_t> value = item_t::get_tag(tag)) return value; if (inherit && xact) return xact->get_tag(tag); return none; } optional<value_t> post_t::get_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask, bool inherit) const { if (optional<value_t> value = item_t::get_tag(tag_mask, value_mask)) return value; if (inherit && xact) return xact->get_tag(tag_mask, value_mask); return none; } date_t post_t::value_date() const { if (xdata_ && is_valid(xdata_->value_date)) return xdata_->value_date; return date(); } date_t post_t::date() const { if (xdata_ && is_valid(xdata_->date)) return xdata_->date; if (item_t::use_aux_date) { if (optional<date_t> aux = aux_date()) return *aux; } return primary_date(); } date_t post_t::primary_date() const { if (xdata_ && is_valid(xdata_->date)) return xdata_->date; if (! _date) { if (xact) return xact->date(); else return CURRENT_DATE(); } return *_date; } optional<date_t> post_t::aux_date() const { optional<date_t> date = item_t::aux_date(); if (! date && xact) return xact->aux_date(); return date; } string post_t::payee_from_tag() const { if (optional<value_t> post_payee = get_tag(_("Payee"))) return post_payee->as_string(); else return ""; } string post_t::payee() const { if (_payee) return *_payee; string post_payee = payee_from_tag(); return post_payee != "" ? post_payee : xact ? xact->payee : ""; } namespace { value_t get_this(post_t& post) { return scope_value(&post); } value_t get_is_calculated(post_t& post) { return post.has_flags(POST_CALCULATED); } value_t get_is_cost_calculated(post_t& post) { return post.has_flags(POST_COST_CALCULATED); } value_t get_virtual(post_t& post) { return post.has_flags(POST_VIRTUAL); } value_t get_real(post_t& post) { return ! post.has_flags(POST_VIRTUAL); } value_t get_xact(post_t& post) { return scope_value(post.xact); } value_t get_xact_id(post_t& post) { return static_cast<long>(post.xact_id()); } value_t get_code(post_t& post) { if (post.xact->code) return string_value(*post.xact->code); else return NULL_VALUE; } value_t get_payee(post_t& post) { return string_value(post.payee()); } value_t get_note(post_t& post) { if (post.note || post.xact->note) { string note = post.note ? *post.note : empty_string; note += post.xact->note ? *post.xact->note : empty_string; return string_value(note); } else { return NULL_VALUE; } } value_t get_magnitude(post_t& post) { return post.xact->magnitude(); } value_t get_amount(post_t& post) { if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) return post.xdata().compound_value; else if (post.amount.is_null()) return 0L; else return post.amount; } value_t get_use_direct_amount(post_t& post) { return post.has_xdata() && post.xdata().has_flags(POST_EXT_DIRECT_AMT); } value_t get_commodity(call_scope_t& args) { if (args.has<amount_t>(0)) { return string_value(args.get<amount_t>(0).commodity().symbol()); } else { post_t& post(args.context<post_t>()); if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) return string_value(post.xdata().compound_value.to_amount() .commodity().symbol()); else return string_value(post.amount.commodity().symbol()); } } value_t get_commodity_is_primary(post_t& post) { if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) return post.xdata().compound_value.to_amount() .commodity().has_flags(COMMODITY_PRIMARY); else return post.amount.commodity().has_flags(COMMODITY_PRIMARY); } value_t get_has_cost(post_t& post) { return post.cost ? true : false; } value_t get_cost(post_t& post) { if (post.cost) return *post.cost; else if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) return post.xdata().compound_value; else if (post.amount.is_null()) return 0L; else return post.amount; } value_t get_price(post_t& post) { if (post.amount.is_null()) return 0L; if (post.amount.has_annotation() && post.amount.annotation().price) return *post.amount.price(); else return get_cost(post); } value_t get_total(post_t& post) { if (post.xdata_ && ! post.xdata_->total.is_null()) return post.xdata_->total; else if (post.amount.is_null()) return 0L; else return post.amount; } value_t get_count(post_t& post) { if (post.xdata_) return long(post.xdata_->count); else return 1L; } value_t get_account(call_scope_t& args) { post_t& post(args.context<post_t>()); account_t& account(*post.reported_account()); string name; if (args.has(0)) { if (args[0].is_long()) { if (args.get<long>(0) > 2) name = format_t::truncate (account.fullname(), static_cast<std::size_t>(args.get<long>(0) - 2), /* account_abbrev_length= */ 2); else name = account.fullname(); } else { account_t * acct = NULL; account_t * master = &account; while (master->parent) master = master->parent; if (args[0].is_string()) { name = args.get<string>(0); acct = master->find_account(name, false); } else if (args[0].is_mask()) { name = args.get<mask_t>(0).str(); acct = master->find_account_re(name); } else { throw_(std::runtime_error, _f("Expected string or mask for argument 1, but received %1%") % args[0].label()); } if (! acct) throw_(std::runtime_error, _f("Could not find an account matching '%1%'") % args[0]); return value_t(static_cast<scope_t *>(acct)); } } else if (args.type_context() == value_t::SCOPE) { return scope_value(&account); } else { name = account.fullname(); } return string_value(name); } value_t get_display_account(call_scope_t& args) { value_t acct = get_account(args); if (acct.is_string()) { post_t& post(args.context<post_t>()); if (post.has_flags(POST_VIRTUAL)) { if (post.must_balance()) acct = string_value(string("[") + acct.as_string() + "]"); else acct = string_value(string("(") + acct.as_string() + ")"); } } return acct; } value_t get_account_id(post_t& post) { return static_cast<long>(post.account_id()); } value_t get_account_base(post_t& post) { return string_value(post.reported_account()->name); } value_t get_account_depth(post_t& post) { return long(post.reported_account()->depth); } value_t get_value_date(post_t& post) { if (post.has_xdata()) { post_t::xdata_t& xdata(post.xdata()); if (! xdata.value_date.is_not_a_date()) return xdata.value_date; } return post.date(); } value_t get_datetime(post_t& post) { return (! post.xdata().datetime.is_not_a_date_time() ? post.xdata().datetime : datetime_t(post.date())); } value_t get_checkin(post_t& post) { return post.checkin ? *post.checkin : NULL_VALUE; } value_t get_checkout(post_t& post) { return post.checkout ? *post.checkout : NULL_VALUE; } template <value_t (*Func)(post_t&)> value_t get_wrapper(call_scope_t& scope) { return (*Func)(find_scope<post_t>(scope)); } value_t fn_any(call_scope_t& args) { post_t& post(args.context<post_t>()); expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(0)); foreach (post_t * p, post.xact->posts) { bind_scope_t bound_scope(args, *p); if (p == &post && args.has<expr_t::ptr_op_t>(1) && ! args.get<expr_t::ptr_op_t>(1) ->calc(bound_scope, args.locus, args.depth).to_boolean()) { // If the user specifies any(EXPR, false), and the context is a // posting, then that posting isn't considered by the test. ; // skip it } else if (expr->calc(bound_scope, args.locus, args.depth).to_boolean()) { return true; } } return false; } value_t fn_all(call_scope_t& args) { post_t& post(args.context<post_t>()); expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(0)); foreach (post_t * p, post.xact->posts) { bind_scope_t bound_scope(args, *p); if (p == &post && args.has<expr_t::ptr_op_t>(1) && ! args.get<expr_t::ptr_op_t>(1) ->calc(bound_scope, args.locus, args.depth).to_boolean()) { // If the user specifies any(EXPR, false), and the context is a // posting, then that posting isn't considered by the test. ; // skip it } else if (! expr->calc(bound_scope, args.locus, args.depth).to_boolean()) { return false; } } return true; } } expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, const string& name) { if (kind != symbol_t::FUNCTION) return item_t::lookup(kind, name); switch (name[0]) { case 'a': if (name[1] == '\0' || name == "amount") return WRAP_FUNCTOR(get_wrapper<&get_amount>); else if (name == "account") return WRAP_FUNCTOR(get_account); else if (name == "account_base") return WRAP_FUNCTOR(get_wrapper<&get_account_base>); else if (name == "account_id") return WRAP_FUNCTOR(get_wrapper<&get_account_id>); else if (name == "any") return WRAP_FUNCTOR(&fn_any); else if (name == "all") return WRAP_FUNCTOR(&fn_all); break; case 'b': if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_cost>); break; case 'c': if (name == "code") return WRAP_FUNCTOR(get_wrapper<&get_code>); else if (name == "cost") return WRAP_FUNCTOR(get_wrapper<&get_cost>); else if (name == "cost_calculated") return WRAP_FUNCTOR(get_wrapper<&get_is_cost_calculated>); else if (name == "count") return WRAP_FUNCTOR(get_wrapper<&get_count>); else if (name == "calculated") return WRAP_FUNCTOR(get_wrapper<&get_is_calculated>); else if (name == "commodity") return WRAP_FUNCTOR(&get_commodity); else if (name == "checkin") return WRAP_FUNCTOR(get_wrapper<&get_checkin>); else if (name == "checkout") return WRAP_FUNCTOR(get_wrapper<&get_checkout>); break; case 'd': if (name == "display_account") return WRAP_FUNCTOR(get_display_account); else if (name == "depth") return WRAP_FUNCTOR(get_wrapper<&get_account_depth>); else if (name == "datetime") return WRAP_FUNCTOR(get_wrapper<&get_datetime>); break; case 'h': if (name == "has_cost") return WRAP_FUNCTOR(get_wrapper<&get_has_cost>); break; case 'i': if (name == "index") return WRAP_FUNCTOR(get_wrapper<&get_count>); break; case 'm': if (name == "magnitude") return WRAP_FUNCTOR(get_wrapper<&get_magnitude>); break; case 'n': if (name == "note") return WRAP_FUNCTOR(get_wrapper<&get_note>); else if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_count>); break; case 'p': if (name == "post") return WRAP_FUNCTOR(get_wrapper<&get_this>); else if (name == "payee") return WRAP_FUNCTOR(get_wrapper<&get_payee>); else if (name == "primary") return WRAP_FUNCTOR(get_wrapper<&get_commodity_is_primary>); else if (name == "price") return WRAP_FUNCTOR(get_wrapper<&get_price>); else if (name == "parent") return WRAP_FUNCTOR(get_wrapper<&get_xact>); break; case 'r': if (name == "real") return WRAP_FUNCTOR(get_wrapper<&get_real>); break; case 't': if (name == "total") return WRAP_FUNCTOR(get_wrapper<&get_total>); break; case 'u': if (name == "use_direct_amount") return WRAP_FUNCTOR(get_wrapper<&get_use_direct_amount>); break; case 'v': if (name == "virtual") return WRAP_FUNCTOR(get_wrapper<&get_virtual>); else if (name == "value_date") return WRAP_FUNCTOR(get_wrapper<&get_value_date>); break; case 'x': if (name == "xact") return WRAP_FUNCTOR(get_wrapper<&get_xact>); else if (name == "xact_id") return WRAP_FUNCTOR(get_wrapper<&get_xact_id>); break; case 'N': if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_count>); break; case 'O': if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_total>); break; case 'R': if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_real>); break; } return item_t::lookup(kind, name); } amount_t post_t::resolve_expr(scope_t& scope, expr_t& expr) { bind_scope_t bound_scope(scope, *this); value_t result(expr.calc(bound_scope)); if (result.is_long()) { return result.to_amount(); } else { if (! result.is_amount()) throw_(amount_error, _("Amount expressions must result in a simple amount")); return result.as_amount(); } } std::size_t post_t::xact_id() const { std::size_t id = 1; foreach (post_t * p, xact->posts) { if (p == this) return id; id++; } assert("Failed to find posting within its transaction" == NULL); return 0; } std::size_t post_t::account_id() const { std::size_t id = 1; foreach (post_t * p, account->posts) { if (p == this) return id; id++; } assert("Failed to find posting within its transaction" == NULL); return 0; } bool post_t::valid() const { if (! xact) { DEBUG("ledger.validate", "post_t: ! xact"); return false; } posts_list::const_iterator i = std::find(xact->posts.begin(), xact->posts.end(), this); if (i == xact->posts.end()) { DEBUG("ledger.validate", "post_t: ! found"); return false; } if (! account) { DEBUG("ledger.validate", "post_t: ! account"); return false; } if (! amount.valid()) { DEBUG("ledger.validate", "post_t: ! amount.valid()"); return false; } if (cost) { if (! cost->valid()) { DEBUG("ledger.validate", "post_t: cost && ! cost->valid()"); return false; } if (! cost->keep_precision()) { DEBUG("ledger.validate", "post_t: ! cost->keep_precision()"); return false; } } return true; } void post_t::add_to_value(value_t& value, const optional<expr_t&>& expr) const { if (xdata_ && xdata_->has_flags(POST_EXT_COMPOUND)) { if (! xdata_->compound_value.is_null()) add_or_set_value(value, xdata_->compound_value); } else if (expr) { bind_scope_t bound_scope(*expr->get_context(), const_cast<post_t&>(*this)); #if 1 value_t temp(expr->calc(bound_scope)); add_or_set_value(value, temp); #else if (! xdata_) xdata_ = xdata_t(); xdata_->value = expr->calc(bound_scope); xdata_->add_flags(POST_EXT_COMPOUND); add_or_set_value(value, xdata_->value); #endif } else if (xdata_ && xdata_->has_flags(POST_EXT_VISITED) && ! xdata_->visited_value.is_null()) { add_or_set_value(value, xdata_->visited_value); } else { add_or_set_value(value, amount); } } void post_t::set_reported_account(account_t * acct) { xdata().account = acct; acct->xdata().reported_posts.push_back(this); } void extend_post(post_t& post, journal_t& journal) { commodity_t& comm(post.amount.commodity()); annotation_t * details = (comm.has_annotation() ? &as_annotated_commodity(comm).details : NULL); if (! details || ! details->value_expr) { optional<expr_t> value_expr; if (optional<value_t> data = post.get_tag(_("Value"))) value_expr = expr_t(data->to_string()); if (! value_expr) value_expr = post.account->value_expr; if (! value_expr) value_expr = post.amount.commodity().value_expr(); if (! value_expr) value_expr = journal.value_expr; if (value_expr) { if (! details) { annotation_t new_details; new_details.value_expr = value_expr; commodity_t * new_comm = commodity_pool_t::current_pool->find_or_create(comm, new_details); post.amount.set_commodity(*new_comm); } else { details->value_expr = value_expr; } } } } void put_post(property_tree::ptree& st, const post_t& post) { if (post.state() == item_t::CLEARED) st.put("<xmlattr>.state", "cleared"); else if (post.state() == item_t::PENDING) st.put("<xmlattr>.state", "pending"); if (post.has_flags(POST_VIRTUAL)) st.put("<xmlattr>.virtual", "true"); if (post.has_flags(ITEM_GENERATED)) st.put("<xmlattr>.generated", "true"); if (post._date) put_date(st.put("date", ""), *post._date); if (post._date_aux) put_date(st.put("aux-date", ""), *post._date_aux); if (post.payee_from_tag() != "") st.put("payee", post.payee_from_tag()); if (post.account) { property_tree::ptree& t(st.put("account", "")); std::ostringstream buf; buf.width(sizeof(intptr_t) * 2); buf.fill('0'); buf << std::hex << reinterpret_cast<intptr_t>(post.account); t.put("<xmlattr>.ref", buf.str()); t.put("name", post.account->fullname()); } { property_tree::ptree& t(st.put("post-amount", "")); if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND)) put_value(t, post.xdata().compound_value); else put_amount(t.put("amount", ""), post.amount); } if (post.cost) put_amount(st.put("cost", ""), *post.cost); if (post.assigned_amount) { if (post.has_flags(POST_CALCULATED)) put_amount(st.put("balance-assertion", ""), *post.assigned_amount); else put_amount(st.put("balance-assignment", ""), *post.assigned_amount); } if (post.note) st.put("note", *post.note); if (post.metadata) put_metadata(st.put("metadata", ""), *post.metadata); if (post.xdata_ && ! post.xdata_->total.is_null()) put_value(st.put("total", ""), post.xdata_->total); } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/post.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000020300�14411236400�0014674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file post.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_POST_H #define INCLUDED_POST_H #include "item.h" namespace ledger { class xact_t; class account_t; class post_t : public item_t { public: #define POST_VIRTUAL 0x0010 // the account was specified with (parens) #define POST_MUST_BALANCE 0x0020 // posting must balance in the transaction #define POST_CALCULATED 0x0040 // posting's amount was calculated #define POST_COST_CALCULATED 0x0080 // posting's cost was calculated #define POST_COST_IN_FULL 0x0100 // cost specified using @@ #define POST_COST_FIXATED 0x0200 // cost is fixed using = indicator #define POST_COST_VIRTUAL 0x0400 // cost is virtualized: (@) #define POST_ANONYMIZED 0x0800 // a temporary, anonymous posting #define POST_DEFERRED 0x1000 // the account was specified with <angles> xact_t * xact; // only set for posts of regular xacts account_t * account; amount_t amount; // can be null until finalization optional<expr_t> amount_expr; optional<amount_t> cost; optional<amount_t> given_cost; optional<amount_t> assigned_amount; optional<datetime_t> checkin; optional<datetime_t> checkout; private: optional<string> _payee; public: post_t(account_t * _account = NULL, flags_t _flags = ITEM_NORMAL) : item_t(_flags), xact(NULL), account(_account) { TRACE_CTOR(post_t, "account_t *, flags_t"); } post_t(account_t * _account, const amount_t& _amount, flags_t _flags = ITEM_NORMAL, const optional<string>& _note = none) : item_t(_flags, _note), xact(NULL), account(_account), amount(_amount) { TRACE_CTOR(post_t, "account_t *, amount_t, flags_t, optional<string>"); } post_t(const post_t& post) : item_t(post), xact(post.xact), account(post.account), amount(post.amount), cost(post.cost), assigned_amount(post.assigned_amount), checkin(post.checkin), checkout(post.checkout), xdata_(post.xdata_) { copy_details(post); TRACE_CTOR(post_t, "copy"); } virtual ~post_t() { TRACE_DTOR(post_t); } virtual string description() { if (pos) { std::ostringstream buf; buf << _f("posting at line %1%") % pos->beg_line; return buf.str(); } else { return string(_("generated posting")); } } virtual bool has_tag(const string& tag, bool inherit = true) const; virtual bool has_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask = none, bool inherit = true) const; virtual optional<value_t> get_tag(const string& tag, bool inherit = true) const; virtual optional<value_t> get_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask = none, bool inherit = true) const; virtual date_t value_date() const; virtual date_t date() const; virtual date_t primary_date() const; virtual optional<date_t> aux_date() const; string payee_from_tag() const; string payee() const; void set_payee(const string& payee) { _payee = payee; } bool must_balance() const { return ! has_flags(POST_VIRTUAL) || has_flags(POST_MUST_BALANCE); } virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); amount_t resolve_expr(scope_t& scope, expr_t& expr); std::size_t xact_id() const; std::size_t account_id() const; virtual void copy_details(const item_t& item) { const post_t& post(dynamic_cast<const post_t&>(item)); xdata_ = post.xdata_; item_t::copy_details(item); } bool valid() const; struct xdata_t : public supports_flags<uint_least16_t> { #define POST_EXT_RECEIVED 0x0001 #define POST_EXT_HANDLED 0x0002 #define POST_EXT_DISPLAYED 0x0004 #define POST_EXT_DIRECT_AMT 0x0008 #define POST_EXT_SORT_CALC 0x0010 #define POST_EXT_COMPOUND 0x0020 #define POST_EXT_VISITED 0x0040 #define POST_EXT_MATCHES 0x0080 #define POST_EXT_CONSIDERED 0x0100 value_t visited_value; value_t compound_value; value_t total; std::size_t count; date_t date; date_t value_date; datetime_t datetime; account_t * account; std::list<sort_value_t> sort_values; xdata_t() : supports_flags<uint_least16_t>(), count(0), account(NULL) { TRACE_CTOR(post_t::xdata_t, ""); } xdata_t(const xdata_t& other) : supports_flags<uint_least16_t>(other.flags()), visited_value(other.visited_value), compound_value(other.compound_value), total(other.total), count(other.count), date(other.date), account(other.account), sort_values(other.sort_values) { TRACE_CTOR(post_t::xdata_t, "copy"); } ~xdata_t() throw() { TRACE_DTOR(post_t::xdata_t); } }; // This variable holds optional "extended data" which is usually produced // only during reporting, and only for the posting set being reported. // It's a memory-saving measure to delay allocation until the last possible // moment. mutable optional<xdata_t> xdata_; bool has_xdata() const { return static_cast<bool>(xdata_); } void clear_xdata() { xdata_ = none; } xdata_t& xdata() { if (! xdata_) xdata_ = xdata_t(); return *xdata_; } const xdata_t& xdata() const { return const_cast<post_t *>(this)->xdata(); } void add_to_value(value_t& value, const optional<expr_t&>& expr = none) const; void set_reported_account(account_t * account); account_t * reported_account() { if (xdata_) if (account_t * acct = xdata_->account) return acct; assert(account); return account; } const account_t * reported_account() const { return const_cast<post_t *>(this)->reported_account(); } friend class xact_t; struct compare_by_date_and_sequence { bool operator()(const post_t * left, const post_t * right) const { gregorian::date_duration duration = left->primary_date() - right->primary_date(); if (duration.days() == 0) { return ((left->pos ? left->pos->sequence : 0) < (right->pos ? right->pos->sequence : 0)); } else { return duration.days() < 0; } } }; }; class journal_t; void extend_post(post_t& post, journal_t& journal); void put_post(property_tree::ptree& pt, const post_t& post); } // namespace ledger #endif // INCLUDED_POST_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/precmd.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000014210�14411236400�0015322�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "precmd.h" #include "xact.h" #include "post.h" #include "account.h" #include "query.h" #include "session.h" #include "report.h" #include "format.h" namespace ledger { namespace { post_t * get_sample_xact(report_t& report) { { string str; { std::ostringstream buf; buf << "2004/05/27 Book Store\n" << " ; This note applies to all postings. :SecondTag:\n" << " Expenses:Books 20 BOOK @ $10\n" << " ; Metadata: Some Value\n" << " ; Typed:: $100 + $200\n" << " ; :ExampleTag:\n" << " ; Here follows a note describing the posting.\n" << " Liabilities:MasterCard $-200.00\n"; str = buf.str(); } std::ostream& out(report.output_stream); out << _("--- Context is first posting of the following transaction ---") << std::endl << str << std::endl; { shared_ptr<std::istringstream> in(new std::istringstream(str)); parse_context_stack_t parsing_context; parsing_context.push(in); parsing_context.get_current().journal = report.session.journal.get(); parsing_context.get_current().scope = &report.session; report.session.journal->read(parsing_context); report.session.journal->clear_xdata(); } } xact_t * first = report.session.journal->xacts.front(); return first->posts.front(); } } value_t parse_command(call_scope_t& args) { string arg = join_args(args); if (arg.empty()) throw std::logic_error(_("Usage: parse TEXT")); report_t& report(find_scope<report_t>(args)); std::ostream& out(report.output_stream); post_t * post = get_sample_xact(report); out << _("--- Input expression ---") << std::endl; out << arg << std::endl; out << std::endl << _("--- Text as parsed ---") << std::endl; expr_t expr(arg); expr.print(out); out << std::endl; out << std::endl << _("--- Expression tree ---") << std::endl; expr.dump(out); bind_scope_t bound_scope(args, *post); expr.compile(bound_scope); out << std::endl << _("--- Compiled tree ---") << std::endl; expr.dump(out); out << std::endl << _("--- Calculated value ---") << std::endl; value_t result(expr.calc()); result.strip_annotations(report.what_to_keep()).dump(out); out << std::endl; return NULL_VALUE; } value_t eval_command(call_scope_t& args) { report_t& report(find_scope<report_t>(args)); expr_t expr(join_args(args)); value_t result(expr.calc(args).strip_annotations(report.what_to_keep())); if (! result.is_null()) report.output_stream << result << std::endl; return NULL_VALUE; } value_t format_command(call_scope_t& args) { string arg = join_args(args); if (arg.empty()) throw std::logic_error(_("Usage: format TEXT")); report_t& report(find_scope<report_t>(args)); std::ostream& out(report.output_stream); post_t * post = get_sample_xact(report); out << _("--- Input format string ---") << std::endl; out << arg << std::endl << std::endl; out << _("--- Format elements ---") << std::endl; format_t fmt(arg); fmt.dump(out); out << std::endl << _("--- Formatted string ---") << std::endl; bind_scope_t bound_scope(args, *post); out << '"'; out << fmt(bound_scope); out << "\"\n"; return NULL_VALUE; } value_t period_command(call_scope_t& args) { string arg = join_args(args); if (arg.empty()) throw std::logic_error(_("Usage: period TEXT")); report_t& report(find_scope<report_t>(args)); std::ostream& out(report.output_stream); show_period_tokens(out, arg); out << std::endl; date_interval_t interval(arg); interval.dump(out); return NULL_VALUE; } value_t query_command(call_scope_t& args) { report_t& report(find_scope<report_t>(args)); std::ostream& out(report.output_stream); out << _("--- Input arguments ---") << std::endl; args.value().dump(out); out << std::endl << std::endl; query_t query(args.value(), report.what_to_keep(), ! report.HANDLED(collapse)); if (query.has_query(query_t::QUERY_LIMIT)) { call_scope_t sub_args(static_cast<scope_t&>(args)); sub_args.push_back(string_value(query.get_query(query_t::QUERY_LIMIT))); parse_command(sub_args); } if (query.has_query(query_t::QUERY_SHOW)) { out << std::endl << _("====== Display predicate ======") << std::endl << std::endl; call_scope_t disp_sub_args(static_cast<scope_t&>(args)); disp_sub_args.push_back(string_value(query.get_query(query_t::QUERY_SHOW))); parse_command(disp_sub_args); } return NULL_VALUE; } } // namespace ledger ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/precmd.h���������������������������������������������������������������������������0000664�0000000�0000000�00000004022�14411236400�0015164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup report */ /** * @file precmd.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_PRECMD_H #define INCLUDED_PRECMD_H #include "value.h" namespace ledger { class call_scope_t; value_t parse_command(call_scope_t& args); value_t eval_command(call_scope_t& args); value_t format_command(call_scope_t& args); value_t period_command(call_scope_t& args); value_t query_command(call_scope_t& args); } // namespace ledger #endif // INCLUDED_PRECMD_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/predicate.h������������������������������������������������������������������������0000664�0000000�0000000�00000006425�14411236400�0015663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file predicate.h * @author John Wiegley * * @ingroup expr */ #ifndef INCLUDED_PREDICATE_H #define INCLUDED_PREDICATE_H #include "expr.h" #include "commodity.h" #include "annotate.h" namespace ledger { class predicate_t : public expr_t { public: keep_details_t what_to_keep; predicate_t(const keep_details_t& _what_to_keep = keep_details_t()) : what_to_keep(_what_to_keep) { TRACE_CTOR(predicate_t, ""); } predicate_t(const predicate_t& other) : expr_t(other), what_to_keep(other.what_to_keep) { TRACE_CTOR(predicate_t, "copy"); } predicate_t(ptr_op_t _ptr, const keep_details_t& _what_to_keep, scope_t * _context = NULL) : expr_t(_ptr, _context), what_to_keep(_what_to_keep) { TRACE_CTOR(predicate_t, "ptr_op_t, keep_details_t, scope_t *"); } predicate_t(const string& str, const keep_details_t& _what_to_keep, const parse_flags_t& flags = PARSE_DEFAULT) : expr_t(str, flags), what_to_keep(_what_to_keep) { TRACE_CTOR(predicate_t, "string, keep_details_t, parse_flags_t"); } predicate_t(std::istream& in, const keep_details_t& _what_to_keep, const parse_flags_t& flags = PARSE_DEFAULT) : expr_t(in, flags), what_to_keep(_what_to_keep) { TRACE_CTOR(predicate_t, "std::istream&, keep_details_t, parse_flags_t"); } virtual ~predicate_t() { TRACE_DTOR(predicate_t); } virtual value_t real_calc(scope_t& scope) { return (*this ? expr_t::real_calc(scope) .strip_annotations(what_to_keep) .to_boolean() : true); } }; } // namespace ledger #endif // INCLUDED_PREDICATE_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/print.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000024141�14411236400�0015210�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "print.h" #include "xact.h" #include "post.h" #include "account.h" #include "session.h" #include "report.h" namespace ledger { namespace { bool post_has_simple_amount(const post_t& post) { // Is the amount the result of a computation, i.e., it wasn't // explicit specified by the user? if (post.has_flags(POST_CALCULATED)) return false; // Is the amount still empty? This shouldn't be true by this point, // but we check anyway for safety. if (post.amount.is_null()) return false; // Is the amount a complex expression. If so, the first 'if' should // have triggered. if (post.amount_expr) return false; // Is there a balance assignment? If so, don't elide the amount as // that can change the semantics. if (post.assigned_amount) return false; // Does it have an explicitly specified cost (i.e., one that wasn't // calculated for the user)? If so, don't elide the amount! if (post.cost && ! post.has_flags(POST_COST_CALCULATED)) return false; return true; } void print_note(std::ostream& out, const string& note, const bool note_on_next_line, const std::size_t columns, const std::size_t prior_width) { // The 3 is for two spaces and a semi-colon before the note. if (note_on_next_line || (columns > 0 && (columns <= prior_width + 3 || note.length() > columns - (prior_width + 3)))) out << "\n ;"; else out << " ;"; bool need_separator = false; for (const char * p = note.c_str(); *p; p++) { if (*p == '\n') { need_separator = true; } else { if (need_separator) { out << "\n ;"; need_separator = false; } out << *p; } } } void print_xact(report_t& report, std::ostream& out, xact_t& xact) { format_type_t format_type = FMT_WRITTEN; string format_str; optional<const char *> format; if (report.HANDLED(date_format_)) { format_type = FMT_CUSTOM; format_str = report.HANDLER(date_format_).str(); format = format_str.c_str(); } std::ostringstream buf; buf << format_date(item_t::use_aux_date ? xact.date() : xact.primary_date(), format_type, format); if (! item_t::use_aux_date && xact.aux_date()) buf << '=' << format_date(*xact.aux_date(), format_type, format); buf << ' '; buf << (xact.state() == item_t::CLEARED ? "* " : (xact.state() == item_t::PENDING ? "! " : "")); if (xact.code) buf << '(' << *xact.code << ") "; buf << xact.payee; string leader = buf.str(); out << leader; std::size_t columns = (report.HANDLED(columns_) ? lexical_cast<std::size_t>(report.HANDLER(columns_).str()) : 80); if (xact.note) print_note(out, *xact.note, xact.has_flags(ITEM_NOTE_ON_NEXT_LINE), columns, unistring(leader).length()); out << '\n'; if (xact.metadata) { foreach (const item_t::string_map::value_type& data, *xact.metadata) { if (! data.second.second) { out << " ; "; if (data.second.first) out << data.first << ": " << *data.second.first; else out << ':' << data.first << ":"; out << '\n'; } } } std::size_t count = xact.posts.size(); std::size_t index = 0; foreach (post_t * post, xact.posts) { index++; if (! report.HANDLED(generated) && (post->has_flags(ITEM_TEMP | ITEM_GENERATED) && ! post->has_flags(POST_ANONYMIZED))) continue; out << " "; std::ostringstream pbuf; if (xact.state() == item_t::UNCLEARED) pbuf << (post->state() == item_t::CLEARED ? "* " : (post->state() == item_t::PENDING ? "! " : "")); if (post->has_flags(POST_VIRTUAL)) { if (post->has_flags(POST_MUST_BALANCE)) pbuf << '['; else pbuf << '('; } pbuf << post->account->fullname(); if (post->has_flags(POST_VIRTUAL)) { if (post->has_flags(POST_MUST_BALANCE)) pbuf << ']'; else pbuf << ')'; } unistring name(pbuf.str()); std::size_t account_width = (report.HANDLED(account_width_) ? lexical_cast<std::size_t>(report.HANDLER(account_width_).str()) : 36); if (account_width < name.length()) account_width = name.length(); if (! post->has_flags(POST_CALCULATED) || report.HANDLED(generated)) { out << name.extract(); std::string::size_type slip = (static_cast<std::string::size_type>(account_width) - static_cast<std::string::size_type>(name.length())); std::size_t amount_width = (report.HANDLED(amount_width_) ? lexical_cast<std::size_t>(report.HANDLER(amount_width_).str()) : 12); string amt; if (post->amount_expr) { std::ostringstream amt_str; justify(amt_str, post->amount_expr->text(), (int)amount_width, true); amt = amt_str.str(); } else if (count == 2 && index == 2 && post_has_simple_amount(*post) && post_has_simple_amount(*(*xact.posts.begin())) && ((*xact.posts.begin())->amount.commodity() == post->amount.commodity())) { // If there are two postings and they both simple amount, and // they are both of the same commodity, don't bother printing // the second amount as it's always just an inverse of the // first. } else { std::ostringstream amt_str; value_t(post->amount).print(amt_str, static_cast<int>(amount_width), -1, AMOUNT_PRINT_RIGHT_JUSTIFY | (report.HANDLED(generated) ? 0 : AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS)); amt = amt_str.str(); } string trimmed_amt(amt); trim_left(trimmed_amt); std::string::size_type amt_slip = (static_cast<std::string::size_type>(amt.length()) - static_cast<std::string::size_type>(trimmed_amt.length())); std::ostringstream amtbuf; if (slip + amt_slip < 2) amtbuf << string(2 - (slip + amt_slip), ' '); amtbuf << amt; if (post->given_cost && ! post->has_flags(POST_CALCULATED | POST_COST_CALCULATED)) { std::string cost_op; if (post->has_flags(POST_COST_IN_FULL)) cost_op = "@@"; else cost_op = "@"; if (post->has_flags(POST_COST_VIRTUAL)) cost_op = "(" + cost_op + ")"; if (post->has_flags(POST_COST_IN_FULL)) amtbuf << " " << cost_op << " " << post->given_cost->abs(); else amtbuf << " " << cost_op << " " << (*post->given_cost / post->amount).abs(); } if (post->assigned_amount) amtbuf << " = " << *post->assigned_amount; string trailer = amtbuf.str(); if (! trailer.empty()) { if (slip > 0) { out.width(static_cast<std::streamsize>(slip)); out << ' '; } out << trailer; account_width += unistring(trailer).length(); } } else { out << pbuf.str(); } if (post->note) print_note(out, *post->note, post->has_flags(ITEM_NOTE_ON_NEXT_LINE), columns, 4 + account_width); out << '\n'; } } } void print_xacts::title(const string&) { if (first_title) { first_title = false; } else { std::ostream& out(report.output_stream); out << '\n'; } } void print_xacts::flush() { std::ostream& out(report.output_stream); bool first = true; foreach (xact_t * xact, xacts) { if (first) first = false; else out << '\n'; if (print_raw) { print_item(out, *xact); out << '\n'; } else { print_xact(report, out, *xact); } } out.flush(); } void print_xacts::operator()(post_t& post) { if (! post.has_xdata() || ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { if (xacts_present.find(post.xact) == xacts_present.end()) { xacts_present.insert(xacts_present_map::value_type(post.xact, true)); xacts.push_back(post.xact); } post.xdata().add_flags(POST_EXT_DISPLAYED); } } } // namespace ledger �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/print.h����������������������������������������������������������������������������0000664�0000000�0000000�00000005202�14411236400�0015047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file convert.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_PRINT_H #define INCLUDED_PRINT_H #include "chain.h" #include "predicate.h" #include "format.h" namespace ledger { class xact_t; class post_t; class report_t; class print_xacts : public item_handler<post_t> { protected: typedef std::list<xact_t *> xacts_list; typedef std::map<xact_t *, bool> xacts_present_map; report_t& report; xacts_present_map xacts_present; xacts_list xacts; bool print_raw; bool first_title; public: print_xacts(report_t& _report, bool _print_raw = false) : report(_report), print_raw(_print_raw), first_title(true) { TRACE_CTOR(print_xacts, "report&, bool"); } virtual ~print_xacts() { TRACE_DTOR(print_xacts); } virtual void title(const string&); virtual void flush(); virtual void operator()(post_t& post); virtual void clear() { xacts_present.clear(); xacts.clear(); item_handler<post_t>::clear(); } }; } // namespace ledger #endif // INCLUDED_PRINT_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/pstream.h��������������������������������������������������������������������������0000664�0000000�0000000�00000006612�14411236400�0015374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup util */ /** * @file pstream.h * @author John Wiegley * * @ingroup util */ #ifndef INCLUDED_PSTREAM_H #define INCLUDED_PSTREAM_H //#include <istream> //#include <streambuf> class ptristream : public std::istream { class ptrinbuf : public std::streambuf { ptrinbuf(const ptrinbuf&); ptrinbuf& operator=(const ptrinbuf&); protected: char * ptr; std::size_t len; public: ptrinbuf(char * _ptr, std::size_t _len) : ptr(_ptr), len(_len) { if (*ptr && len == 0) len = std::strlen(ptr); setg(ptr, // beginning of putback area ptr, // read position ptr+len); // end position TRACE_CTOR(ptrinbuf, "char *, std::size_t"); } ~ptrinbuf() throw() { TRACE_DTOR(ptrinbuf); } protected: virtual int_type underflow() { // is read position before end of buffer? if (gptr() < egptr()) return traits_type::to_int_type(*gptr()); else return EOF; } virtual pos_type seekoff(off_type off, ios_base::seekdir way, ios_base::openmode) { // cast to avoid gcc '-Wswitch' warning // as ios_base::beg/cur/end are not necessarily values of 'way' enum type ios_base::seekdir // based on https://svn.boost.org/trac/boost/ticket/7644 switch (static_cast<int>(way)) { case std::ios::cur: setg(ptr, gptr()+off, ptr+len); break; case std::ios::beg: setg(ptr, ptr+off, ptr+len); break; case std::ios::end: setg(ptr, egptr()+off, ptr+len); break; } return pos_type(gptr() - ptr); } }; protected: ptrinbuf buf; public: ptristream(char * ptr, std::size_t len = 0) : std::istream(0), buf(ptr, len) { rdbuf(&buf); } }; #endif // INCLUDED_PSTREAM_H ����������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/ptree.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000007377�14411236400�0015207�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "ptree.h" #include "xact.h" #include "post.h" #include "account.h" #include "session.h" #include "report.h" namespace ledger { namespace { bool account_visited_p(const account_t& acct) { return ((acct.has_xdata() && acct.xdata().has_flags(ACCOUNT_EXT_VISITED)) || acct.children_with_flags(ACCOUNT_EXT_VISITED)); } } void format_ptree::flush() { std::ostream& out(report.output_stream); property_tree::ptree pt; pt.put("ledger.<xmlattr>.version", lexical_cast<string>((Ledger_VERSION_MAJOR << 16) | (Ledger_VERSION_MINOR << 8) | Ledger_VERSION_PATCH)); property_tree::ptree& ct(pt.put("ledger.commodities", "")); foreach (const commodities_pair& pair, commodities) put_commodity(ct.add("commodity", ""), *pair.second, true); property_tree::ptree& at(pt.put("ledger.accounts", "")); put_account(at.add("account", ""), *report.session.journal->master, account_visited_p); property_tree::ptree& tt(pt.put("ledger.transactions", "")); foreach (const xact_t * xact, transactions) { property_tree::ptree& t(tt.add("transaction", "")); put_xact(t, *xact); property_tree::ptree& post_tree(t.put("postings", "")); foreach (const post_t * post, xact->posts) if (post->has_xdata() && post->xdata().has_flags(POST_EXT_VISITED)) put_post(post_tree.add("posting", ""), *post); } switch (format) { case FORMAT_XML: #if BOOST_VERSION >= 105600 auto indented = property_tree::xml_writer_make_settings<std::string> (' ', 2); #else property_tree::xml_writer_settings<char> indented(' ', 2); #endif property_tree::write_xml(out, pt, indented); out << std::endl; break; } } void format_ptree::operator()(post_t& post) { assert(post.xdata().has_flags(POST_EXT_VISITED)); commodities.insert(commodities_pair(post.amount.commodity().symbol(), &post.amount.commodity())); std::pair<std::set<xact_t *>::iterator, bool> result = transactions_set.insert(post.xact); if (result.second) // we haven't seen this transaction before transactions.push_back(post.xact); } } // namespace ledger �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/ptree.h����������������������������������������������������������������������������0000664�0000000�0000000�00000005342�14411236400�0015037�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup report */ /** * @file ptree.h * @author John Wiegley * * @ingroup report * * @brief Brief * * Long. */ #ifndef INCLUDED_PTREE_H #define INCLUDED_PTREE_H #include "chain.h" namespace ledger { class xact_t; class account_t; class commodity_t; class post_t; class report_t; /** * @brief Brief * * Long. */ class format_ptree : public item_handler<post_t> { protected: report_t& report; typedef std::map<string, commodity_t *> commodities_map; typedef std::pair<string, commodity_t *> commodities_pair; commodities_map commodities; std::set<xact_t *> transactions_set; std::deque<xact_t *> transactions; public: enum format_t { FORMAT_XML } format; format_ptree(report_t& _report, format_t _format = FORMAT_XML) : report(_report), format(_format) { TRACE_CTOR(format_ptree, "report&, format_t"); } virtual ~format_ptree() { TRACE_DTOR(format_ptree); } virtual void flush(); virtual void operator()(post_t& post); virtual void clear() { commodities.clear(); transactions_set.clear(); transactions.clear(); item_handler<post_t>::clear(); } }; } // namespace ledger #endif // INCLUDED_PTREE_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_account.cc����������������������������������������������������������������������0000664�0000000�0000000�00000022103�14411236400�0016214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "account.h" #include "post.h" #include "expr.h" namespace ledger { using namespace boost::python; namespace { long accounts_len(account_t& account) { return static_cast<long>(account.accounts.size()); } account_t& accounts_getitem(account_t& account, long i) { static long last_index = 0; static account_t * last_account = NULL; static accounts_map::iterator elem; long len = static_cast<long>(account.accounts.size()); if (labs(i) >= len) { PyErr_SetString(PyExc_IndexError, _("Index out of range")); throw_error_already_set(); } if (&account == last_account && i == last_index + 1) { last_index = i; return *(*++elem).second; } long x = i < 0 ? len + i : i; elem = account.accounts.begin(); while (--x >= 0) elem++; last_account = &account; last_index = i; return *(*elem).second; } #if 0 account_t * py_find_account_1(journal_t& journal, const string& name) { return journal.find_account(name); } account_t * py_find_account_2(journal_t& journal, const string& name, const bool auto_create) { return journal.find_account(name, auto_create); } #endif account_t::xdata_t& py_xdata(account_t& account) { return account.xdata(); } PyObject * py_account_unicode(account_t& account) { return str_to_py_unicode(account.fullname()); } value_t py_amount_0(const account_t& account) { return account.amount(); } value_t py_amount_1(const account_t& account, const boost::optional<expr_t&>& expr) { return account.amount(false, expr); } value_t py_total_0(const account_t& account) { return account.total(); } value_t py_total_1(const account_t& account, const boost::optional<expr_t&>& expr) { return account.total(expr); } } // unnamed namespace void export_account() { scope().attr("ACCOUNT_EXT_SORT_CALC") = ACCOUNT_EXT_SORT_CALC; scope().attr("ACCOUNT_EXT_HAS_NON_VIRTUALS") = ACCOUNT_EXT_HAS_NON_VIRTUALS; scope().attr("ACCOUNT_EXT_HAS_UNB_VIRTUALS") = ACCOUNT_EXT_HAS_UNB_VIRTUALS; scope().attr("ACCOUNT_EXT_AUTO_VIRTUALIZE") = ACCOUNT_EXT_AUTO_VIRTUALIZE; scope().attr("ACCOUNT_EXT_VISITED") = ACCOUNT_EXT_VISITED; scope().attr("ACCOUNT_EXT_MATCHING") = ACCOUNT_EXT_MATCHING; scope().attr("ACCOUNT_EXT_TO_DISPLAY") = ACCOUNT_EXT_TO_DISPLAY; scope().attr("ACCOUNT_EXT_DISPLAYED") = ACCOUNT_EXT_DISPLAYED; class_< account_t::xdata_t::details_t > ("AccountXDataDetails") .def_readonly("total", &account_t::xdata_t::details_t::total) .def_readonly("real_total", &account_t::xdata_t::details_t::real_total) .def_readonly("calculated", &account_t::xdata_t::details_t::calculated) .def_readonly("gathered", &account_t::xdata_t::details_t::gathered) .def_readonly("posts_count", &account_t::xdata_t::details_t::posts_count) .def_readonly("posts_virtuals_count", &account_t::xdata_t::details_t::posts_virtuals_count) .def_readonly("posts_cleared_count", &account_t::xdata_t::details_t::posts_cleared_count) .def_readonly("posts_last_7_count", &account_t::xdata_t::details_t::posts_last_7_count) .def_readonly("posts_last_30_count", &account_t::xdata_t::details_t::posts_last_30_count) .def_readonly("posts_this_month_count", &account_t::xdata_t::details_t::posts_this_month_count) .def_readonly("earliest_post", &account_t::xdata_t::details_t::earliest_post) .def_readonly("earliest_cleared_post", &account_t::xdata_t::details_t::earliest_cleared_post) .def_readonly("latest_post", &account_t::xdata_t::details_t::latest_post) .def_readonly("latest_cleared_post", &account_t::xdata_t::details_t::latest_cleared_post) .def_readonly("filenames", &account_t::xdata_t::details_t::filenames) .def_readonly("accounts_referenced", &account_t::xdata_t::details_t::accounts_referenced) .def_readonly("payees_referenced", &account_t::xdata_t::details_t::payees_referenced) .def(self += self) .def("update", &account_t::xdata_t::details_t::update) ; class_< account_t::xdata_t > ("AccountXData") #if 1 .add_property("flags", &supports_flags<uint_least16_t>::flags, &supports_flags<uint_least16_t>::set_flags) .def("has_flags", &supports_flags<uint_least16_t>::has_flags) .def("clear_flags", &supports_flags<uint_least16_t>::clear_flags) .def("add_flags", &supports_flags<uint_least16_t>::add_flags) .def("drop_flags", &supports_flags<uint_least16_t>::drop_flags) #endif .def_readonly("self_details", &account_t::xdata_t::self_details) .def_readonly("family_details", &account_t::xdata_t::family_details) .def_readonly("reported_posts", &account_t::xdata_t::reported_posts) .def_readonly("sort_values", &account_t::xdata_t::sort_values) ; scope().attr("ACCOUNT_NORMAL") = ACCOUNT_NORMAL; scope().attr("ACCOUNT_KNOWN") = ACCOUNT_KNOWN; scope().attr("ACCOUNT_TEMP") = ACCOUNT_TEMP; class_< account_t > ("Account") #if 1 .add_property("flags", &supports_flags<>::flags, &supports_flags<>::set_flags) .def("has_flags", &supports_flags<>::has_flags) .def("clear_flags", &supports_flags<>::clear_flags) .def("add_flags", &supports_flags<>::add_flags) .def("drop_flags", &supports_flags<>::drop_flags) #endif .add_property("parent", make_getter(&account_t::parent, return_internal_reference<>())) .def_readwrite("name", &account_t::name) .def_readwrite("note", &account_t::note) .def_readonly("depth", &account_t::depth) .def("__str__", &account_t::fullname) .def("__unicode__", py_account_unicode) .def("fullname", &account_t::fullname) .def("partial_name", &account_t::partial_name) .def("add_account", &account_t::add_account) .def("remove_account", &account_t::remove_account) .def("find_account", &account_t::find_account, return_internal_reference<>()) .def("find_account_re", &account_t::find_account, return_internal_reference<>()) .def("add_post", &account_t::add_post) .def("remove_post", &account_t::remove_post) .def("valid", &account_t::valid) .def("__len__", accounts_len) .def("__getitem__", accounts_getitem, return_internal_reference<>()) .def("__iter__", python::range<return_internal_reference<> > (&account_t::accounts_begin, &account_t::accounts_end)) .def("accounts", python::range<return_internal_reference<> > (&account_t::accounts_begin, &account_t::accounts_end)) .def("posts", python::range<return_internal_reference<> > (&account_t::posts_begin, &account_t::posts_end)) .def("has_xdata", &account_t::has_xdata) .def("clear_xdata", &account_t::clear_xdata) .def("xdata", py_xdata, return_internal_reference<>()) .def("amount", py_amount_0) .def("amount", py_amount_1, args("expr")) .def("total", py_total_0) .def("total", py_total_1, args("expr")) .def("self_details", &account_t::self_details, return_internal_reference<>()) .def("family_details", &account_t::family_details, return_internal_reference<>()) .def("has_xflags", &account_t::has_xflags) .def("children_with_flags", &account_t::children_with_flags) ; } } // namespace ledger �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_amount.cc�����������������������������������������������������������������������0000664�0000000�0000000�00000023171�14411236400�0016071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "commodity.h" #include "annotate.h" #include "pool.h" namespace ledger { using namespace boost::python; namespace { boost::optional<amount_t> py_value_0(const amount_t& amount) { return amount.value(CURRENT_TIME()); } boost::optional<amount_t> py_value_1(const amount_t& amount, const commodity_t * in_terms_of) { return amount.value(CURRENT_TIME(), in_terms_of); } boost::optional<amount_t> py_value_2(const amount_t& amount, const commodity_t * in_terms_of, const datetime_t& moment) { return amount.value(moment, in_terms_of); } boost::optional<amount_t> py_value_2d(const amount_t& amount, const commodity_t * in_terms_of, const date_t& moment) { return amount.value(datetime_t(moment), in_terms_of); } void py_parse_str_1(amount_t& amount, const string& str) { amount.parse(str); } void py_parse_str_2(amount_t& amount, const string& str, unsigned char flags) { amount.parse(str, flags); } #if 0 void py_print(amount_t& amount, object out) { if (PyFile_Check(out.ptr())) { pyofstream outstr(reinterpret_cast<PyFileObject *>(out.ptr())); amount.print(outstr); } else { PyErr_SetString(PyExc_IOError, _("Argument to amount.print_(file) is not a file object")); } } #endif annotation_t& py_amount_annotation(amount_t& amount) { return amount.annotation(); } amount_t py_strip_annotations_0(amount_t& amount) { return amount.strip_annotations(keep_details_t()); } amount_t py_strip_annotations_1(amount_t& amount, const keep_details_t& keep) { return amount.strip_annotations(keep); } PyObject * py_amount_unicode(amount_t& amount) { return str_to_py_unicode(amount.to_string()); } } // unnamed namespace #define EXC_TRANSLATOR(type) \ void exc_translate_ ## type(const type& err) { \ PyErr_SetString(PyExc_ArithmeticError, err.what()); \ } EXC_TRANSLATOR(amount_error) void export_amount() { class_< amount_t > ("Amount") .def("initialize", &amount_t::initialize) // only for the PyUnitTests .staticmethod("initialize") .def("shutdown", &amount_t::shutdown) .staticmethod("shutdown") .add_static_property("is_initialized", make_getter(&amount_t::is_initialized), make_setter(&amount_t::is_initialized)) .add_static_property("stream_fullstrings", make_getter(&amount_t::stream_fullstrings), make_setter(&amount_t::stream_fullstrings)) .def(init<long>()) .def(init<std::string>()) .def("exact", &amount_t::exact, args("value"), _("Construct an amount object whose display precision is always equal to its\n\ internal precision.")) .staticmethod("exact") .def(init<amount_t>()) .def("compare", &amount_t::compare, args("amount"), _("Compare two amounts for equality, returning <0, 0 or >0.")) .def(self == self) .def(self == long()) .def(long() == self) .def(self != self) .def(self != long()) .def(long() != self) .def(! self) .def(self < self) .def(self < long()) .def(long() < self) .def(self <= self) .def(self <= long()) .def(long() <= self) .def(self > self) .def(self > long()) .def(long() > self) .def(self >= self) .def(self >= long()) .def(long() >= self) .def(self += self) .def(self += long()) .def(self + self) .def(self + long()) .def(long() + self) .def(self -= self) .def(self -= long()) .def(self - self) .def(self - long()) .def(long() - self) .def(self *= self) .def(self *= long()) .def(self * self) .def(self * long()) .def(long() * self) .def(self /= self) .def(self /= long()) .def(self / self) .def(self / long()) .def(long() / self) .add_property("precision", &amount_t::precision) .add_property("display_precision", &amount_t::display_precision) .add_property("keep_precision", &amount_t::keep_precision, &amount_t::set_keep_precision) .def("negated", &amount_t::negated) .def("in_place_negate", &amount_t::in_place_negate, return_internal_reference<>()) .def(- self) .def("abs", &amount_t::abs) .def("__abs__", &amount_t::abs) .def("inverted", &amount_t::inverted) .def("rounded", &amount_t::rounded) .def("in_place_round", &amount_t::in_place_round, return_internal_reference<>()) .def("truncated", &amount_t::truncated) .def("in_place_truncate", &amount_t::in_place_truncate, return_internal_reference<>()) .def("floored", &amount_t::floored) .def("in_place_floor", &amount_t::in_place_floor, return_internal_reference<>()) .def("unrounded", &amount_t::unrounded) .def("in_place_unround", &amount_t::in_place_unround, return_internal_reference<>()) .def("reduced", &amount_t::reduced) .def("in_place_reduce", &amount_t::in_place_reduce, return_internal_reference<>()) .def("unreduced", &amount_t::unreduced) .def("in_place_unreduce", &amount_t::in_place_unreduce, return_internal_reference<>()) .def("value", py_value_0) .def("value", py_value_1, args("in_terms_of")) .def("value", py_value_2, args("in_terms_of", "moment")) .def("value", py_value_2d, args("in_terms_of", "moment")) .def("price", &amount_t::price) .def("sign", &amount_t::sign) .def("__nonzero__", &amount_t::is_nonzero) .def("is_nonzero", &amount_t::is_nonzero) .def("is_zero", &amount_t::is_zero) .def("is_realzero", &amount_t::is_realzero) .def("is_null", &amount_t::is_null) .def("to_double", &amount_t::to_double) .def("__float__", &amount_t::to_double) .def("to_long", &amount_t::to_long) .def("__int__", &amount_t::to_long) .def("fits_in_long", &amount_t::fits_in_long) .def("__str__", &amount_t::to_string) .def("to_string", &amount_t::to_string) .def("__unicode__", py_amount_unicode) .def("to_fullstring", &amount_t::to_fullstring) .def("__repr__", &amount_t::to_fullstring) .def("quantity_string", &amount_t::quantity_string) .add_property("commodity", make_function(&amount_t::commodity, return_internal_reference<>()), make_function(&amount_t::set_commodity, with_custodian_and_ward<1, 2>())) .def("has_commodity", &amount_t::has_commodity) .def("with_commodity", &amount_t::with_commodity) .def("clear_commodity", &amount_t::clear_commodity) .def("number", &amount_t::number) .def("annotate", &amount_t::annotate) .def("has_annotation", &amount_t::has_annotation) .add_property("annotation", make_function(py_amount_annotation, return_internal_reference<>())) .def("strip_annotations", py_strip_annotations_0) .def("strip_annotations", py_strip_annotations_1) .def("parse", py_parse_str_1) .def("parse", py_parse_str_2) .def("parse_conversion", &amount_t::parse_conversion) .staticmethod("parse_conversion") .def("valid", &amount_t::valid) ; enum_< parse_flags_enum_t >("ParseFlags") .value("Default", PARSE_DEFAULT) .value("Partial", PARSE_PARTIAL) .value("Single", PARSE_SINGLE) .value("NoMigrate", PARSE_NO_MIGRATE) .value("NoReduce", PARSE_NO_REDUCE) .value("NoAssign", PARSE_NO_ASSIGN) .value("OpContext", PARSE_OP_CONTEXT) .value("SoftFail", PARSE_SOFT_FAIL) ; register_optional_to_python<amount_t>(); implicitly_convertible<long, amount_t>(); implicitly_convertible<string, amount_t>(); #define EXC_TRANSLATE(type) \ register_exception_translator<type>(&exc_translate_ ## type); EXC_TRANSLATE(amount_error); } } // namespace ledger �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_balance.cc����������������������������������������������������������������������0000664�0000000�0000000�00000017222�14411236400�0016153�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "commodity.h" #include "annotate.h" #include "balance.h" namespace ledger { using namespace boost::python; namespace { boost::optional<balance_t> py_value_0(const balance_t& balance) { return balance.value(CURRENT_TIME()); } boost::optional<balance_t> py_value_1(const balance_t& balance, const commodity_t * in_terms_of) { return balance.value(CURRENT_TIME(), in_terms_of); } boost::optional<balance_t> py_value_2(const balance_t& balance, const commodity_t * in_terms_of, const datetime_t& moment) { return balance.value(moment, in_terms_of); } boost::optional<balance_t> py_value_2d(const balance_t& balance, const commodity_t * in_terms_of, const date_t& moment) { return balance.value(datetime_t(moment), in_terms_of); } boost::optional<amount_t> py_commodity_amount_0(const balance_t& balance) { return balance.commodity_amount(); } boost::optional<amount_t> py_commodity_amount_1(const balance_t& balance, const commodity_t& commodity) { return balance.commodity_amount(commodity); } #if 0 void py_print(balance_t& balance, object out) { if (PyFile_Check(out.ptr())) { pyofstream outstr(reinterpret_cast<PyFileObject *>(out.ptr())); balance.print(outstr); } else { PyErr_SetString(PyExc_IOError, _("Argument to balance.print_(file) is not a file object")); } } #endif long balance_len(balance_t& bal) { return static_cast<long>(bal.amounts.size()); } amount_t balance_getitem(balance_t& bal, long i) { long len = static_cast<long>(bal.amounts.size()); if (labs(i) >= len) { PyErr_SetString(PyExc_IndexError, _("Index out of range")); throw_error_already_set(); } long x = i < 0 ? len + i : i; balance_t::amounts_map::iterator elem = bal.amounts.begin(); while (--x >= 0) elem++; return (*elem).second; } balance_t py_strip_annotations_0(balance_t& balance) { return balance.strip_annotations(keep_details_t()); } balance_t py_strip_annotations_1(balance_t& balance, const keep_details_t& keep) { return balance.strip_annotations(keep); } PyObject * py_balance_unicode(balance_t& balance) { return str_to_py_unicode(balance.to_string()); } } // unnamed namespace #define EXC_TRANSLATOR(type) \ void exc_translate_ ## type(const type& err) { \ PyErr_SetString(PyExc_ArithmeticError, err.what()); \ } EXC_TRANSLATOR(balance_error) void export_balance() { class_< balance_t > ("Balance") .def(init<balance_t>()) .def(init<amount_t>()) .def(init<long>()) .def(init<string>()) .def(self += self) .def(self += other<amount_t>()) .def(self += long()) .def(self + self) .def(self + other<amount_t>()) .def(self + long()) .def(self -= self) .def(self -= other<amount_t>()) .def(self -= long()) .def(self - self) .def(self - other<amount_t>()) .def(self - long()) .def(self *= other<amount_t>()) .def(self *= long()) .def(self * other<amount_t>()) .def(self * long()) .def(self /= other<amount_t>()) .def(self /= long()) .def(self / other<amount_t>()) .def(self / long()) .def(- self) .def(self == self) .def(self == other<amount_t>()) .def(self == long()) .def(self != self) .def(self != other<amount_t>()) .def(self != long()) .def(! self) .def("__str__", &balance_t::to_string) .def("to_string", &balance_t::to_string) .def("__unicode__", py_balance_unicode) .def("negated", &balance_t::negated) .def("in_place_negate", &balance_t::in_place_negate, return_internal_reference<>()) .def(- self) .def("abs", &balance_t::abs) .def("__abs__", &balance_t::abs) .def("__len__", balance_len) .def("__getitem__", balance_getitem) .def("rounded", &balance_t::rounded) .def("in_place_round", &balance_t::in_place_round, return_internal_reference<>()) .def("truncated", &balance_t::truncated) .def("in_place_truncate", &balance_t::in_place_truncate, return_internal_reference<>()) .def("floored", &balance_t::floored) .def("in_place_floor", &balance_t::in_place_floor, return_internal_reference<>()) .def("unrounded", &balance_t::unrounded) .def("in_place_unround", &balance_t::in_place_unround, return_internal_reference<>()) .def("reduced", &balance_t::reduced) .def("in_place_reduce", &balance_t::in_place_reduce, return_internal_reference<>()) .def("unreduced", &balance_t::unreduced) .def("in_place_unreduce", &balance_t::in_place_unreduce, return_internal_reference<>()) .def("value", py_value_0) .def("value", py_value_1, args("in_terms_of")) .def("value", py_value_2, args("in_terms_of", "moment")) .def("value", py_value_2d, args("in_terms_of", "moment")) .def("__nonzero__", &balance_t::is_nonzero) .def("is_nonzero", &balance_t::is_nonzero) .def("is_zero", &balance_t::is_zero) .def("is_realzero", &balance_t::is_realzero) .def("is_empty", &balance_t::is_empty) .def("single_amount", &balance_t::single_amount) .def("to_amount", &balance_t::to_amount) .def("commodity_count", &balance_t::commodity_count) .def("commodity_amount", py_commodity_amount_0) .def("commodity_amount", py_commodity_amount_1) .def("number", &balance_t::number) .def("strip_annotations", py_strip_annotations_0) .def("strip_annotations", py_strip_annotations_1) .def("valid", &balance_t::valid) ; register_optional_to_python<balance_t>(); implicitly_convertible<long, balance_t>(); implicitly_convertible<string, balance_t>(); implicitly_convertible<amount_t, balance_t>(); #define EXC_TRANSLATE(type) \ register_exception_translator<type>(&exc_translate_ ## type); EXC_TRANSLATE(balance_error); } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_commodity.cc��������������������������������������������������������������������0000664�0000000�0000000�00000042552�14411236400�0016576�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "commodity.h" #include "annotate.h" #include "pool.h" namespace ledger { using namespace boost::python; namespace { commodity_t * py_create_1(commodity_pool_t& pool, const string& symbol) { return pool.create(symbol); } commodity_t * py_create_2(commodity_pool_t& pool, const string& symbol, const annotation_t& details) { return pool.create(symbol, details); } commodity_t * py_find_or_create_1(commodity_pool_t& pool, const string& symbol) { return pool.find_or_create(symbol); } commodity_t * py_find_or_create_2(commodity_pool_t& pool, const string& symbol, const annotation_t& details) { return pool.find_or_create(symbol, details); } commodity_t * py_find_1(commodity_pool_t& pool, const string& name) { return pool.find(name); } commodity_t * py_find_2(commodity_pool_t& pool, const string& symbol, const annotation_t& details) { return pool.find(symbol, details); } // Exchange one commodity for another, while recording the factored price. void py_exchange_2(commodity_pool_t& pool, commodity_t& commodity, const amount_t& per_unit_cost) { pool.exchange(commodity, per_unit_cost, CURRENT_TIME()); } void py_exchange_3(commodity_pool_t& pool, commodity_t& commodity, const amount_t& per_unit_cost, const datetime_t& moment) { pool.exchange(commodity, per_unit_cost, moment); } cost_breakdown_t py_exchange_7(commodity_pool_t& pool, const amount_t& amount, const amount_t& cost, const bool is_per_unit, const bool add_prices, const boost::optional<datetime_t>& moment, const boost::optional<string>& tag) { return pool.exchange(amount, cost, is_per_unit, add_prices, moment, tag); } commodity_t * py_pool_getitem(commodity_pool_t& pool, const string& symbol) { commodity_pool_t::commodities_map::iterator i = pool.commodities.find(symbol); if (i == pool.commodities.end()) { PyErr_SetString(PyExc_ValueError, (string("Could not find commodity ") + symbol).c_str()); throw_error_already_set(); } return (*i).second.get(); } python::list py_pool_keys(commodity_pool_t& pool) { python::list keys; BOOST_REVERSE_FOREACH (const commodity_pool_t::commodities_map::value_type& pair, pool.commodities) { keys.insert(0, pair.first); } return keys; } bool py_pool_contains(commodity_pool_t& pool, const string& symbol) { return pool.commodities.find(symbol) != pool.commodities.end(); } commodity_pool_t::commodities_map::iterator py_pool_commodities_begin(commodity_pool_t& pool) { return pool.commodities.begin(); } commodity_pool_t::commodities_map::iterator py_pool_commodities_end(commodity_pool_t& pool) { return pool.commodities.end(); } typedef transform_iterator <function<string(commodity_pool_t::commodities_map::value_type&)>, commodity_pool_t::commodities_map::iterator> commodities_map_firsts_iterator; commodities_map_firsts_iterator py_pool_commodities_keys_begin(commodity_pool_t& pool) { return make_transform_iterator (pool.commodities.begin(), boost::bind(&commodity_pool_t::commodities_map::value_type::first, _1)); } commodities_map_firsts_iterator py_pool_commodities_keys_end(commodity_pool_t& pool) { return make_transform_iterator (pool.commodities.end(), boost::bind(&commodity_pool_t::commodities_map::value_type::first, _1)); } typedef transform_iterator <function<commodity_t *(commodity_pool_t::commodities_map::value_type&)>, commodity_pool_t::commodities_map::iterator> commodities_map_seconds_iterator; commodities_map_seconds_iterator py_pool_commodities_values_begin(commodity_pool_t& pool) { return make_transform_iterator (pool.commodities.begin(), boost::bind(&shared_ptr<commodity_t>::get, boost::bind(&commodity_pool_t::commodities_map::value_type::second, _1))); } commodities_map_seconds_iterator py_pool_commodities_values_end(commodity_pool_t& pool) { return make_transform_iterator (pool.commodities.end(), boost::bind(&shared_ptr<commodity_t>::get, boost::bind(&commodity_pool_t::commodities_map::value_type::second, _1))); } void py_add_price_2(commodity_t& commodity, const datetime_t& date, const amount_t& price) { commodity.add_price(date, price); } void py_add_price_3(commodity_t& commodity, const datetime_t& date, const amount_t& price, const bool reflexive) { commodity.add_price(date, price, reflexive); } bool py_keep_all_0(keep_details_t& details) { return details.keep_all(); } bool py_keep_all_1(keep_details_t& details, const commodity_t& comm) { return details.keep_all(comm); } bool py_keep_any_0(keep_details_t& details) { return details.keep_any(); } bool py_keep_any_1(keep_details_t& details, const commodity_t& comm) { return details.keep_any(comm); } commodity_t& py_commodity_referent(commodity_t& comm) { return comm.referent(); } commodity_t& py_annotated_commodity_referent(annotated_commodity_t& comm) { return comm.referent(); } commodity_t& py_strip_annotations_0(commodity_t& comm) { return comm.strip_annotations(keep_details_t()); } commodity_t& py_strip_annotations_1(commodity_t& comm, const keep_details_t& keep) { return comm.strip_annotations(keep); } commodity_t& py_strip_ann_annotations_0(annotated_commodity_t& comm) { return comm.strip_annotations(keep_details_t()); } commodity_t& py_strip_ann_annotations_1(annotated_commodity_t& comm, const keep_details_t& keep) { return comm.strip_annotations(keep); } boost::optional<amount_t> py_price(annotation_t& ann) { return ann.price; } boost::optional<amount_t> py_set_price(annotation_t& ann, const boost::optional<amount_t>& price) { return ann.price = price; } PyObject * py_commodity_unicode(commodity_t& commodity) { return str_to_py_unicode(commodity.symbol()); } } // unnamed namespace void export_commodity() { class_< commodity_pool_t, shared_ptr<commodity_pool_t>, boost::noncopyable > ("CommodityPool", no_init) .add_property("null_commodity", make_getter(&commodity_pool_t::null_commodity, return_internal_reference<>())) .add_property("default_commodity", make_getter(&commodity_pool_t::default_commodity, return_internal_reference<>()), make_setter(&commodity_pool_t::default_commodity, with_custodian_and_ward<1, 2>())) .add_property("keep_base", make_getter(&commodity_pool_t::keep_base), make_setter(&commodity_pool_t::keep_base)) .add_property("price_db", make_getter(&commodity_pool_t::price_db, return_value_policy<return_by_value>()), make_setter(&commodity_pool_t::price_db, return_value_policy<return_by_value>())) .add_property("quote_leeway", make_getter(&commodity_pool_t::quote_leeway), make_setter(&commodity_pool_t::quote_leeway)) .add_property("get_quotes", make_getter(&commodity_pool_t::get_quotes), make_setter(&commodity_pool_t::get_quotes)) .add_property("get_commodity_quote", make_getter(&commodity_pool_t::get_commodity_quote), make_setter(&commodity_pool_t::get_commodity_quote)) .def("create", py_create_1, return_internal_reference<>()) .def("create", py_create_2, return_internal_reference<>()) .def("find_or_create", py_find_or_create_1, return_internal_reference<>()) .def("find_or_create", py_find_or_create_2, return_internal_reference<>()) .def("find", py_find_1, return_internal_reference<>()) .def("find", py_find_2, return_internal_reference<>()) .def("exchange", py_exchange_2, with_custodian_and_ward<1, 2>()) .def("exchange", py_exchange_3, with_custodian_and_ward<1, 2>()) .def("exchange", py_exchange_7) .def("parse_price_directive", &commodity_pool_t::parse_price_directive) .def("parse_price_expression", &commodity_pool_t::parse_price_expression, return_internal_reference<>()) .def("__getitem__", py_pool_getitem, return_internal_reference<>()) .def("keys", py_pool_keys) .def("has_key", py_pool_contains) .def("__contains__", py_pool_contains) .def("__iter__", python::range<return_internal_reference<> > (py_pool_commodities_begin, py_pool_commodities_end)) .def("iteritems", python::range<return_internal_reference<> > (py_pool_commodities_begin, py_pool_commodities_end)) .def("iterkeys", python::range<>(py_pool_commodities_keys_begin, py_pool_commodities_keys_end)) .def("itervalues", python::range<return_internal_reference<> > (py_pool_commodities_values_begin, py_pool_commodities_values_end)) ; map_value_type_converter<commodity_pool_t::commodities_map>(); scope().attr("commodities") = commodity_pool_t::current_pool; scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS; scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED; scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED; scope().attr("COMMODITY_STYLE_DECIMAL_COMMA") = COMMODITY_STYLE_DECIMAL_COMMA; scope().attr("COMMODITY_STYLE_TIME_COLON") = COMMODITY_STYLE_TIME_COLON; scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS; scope().attr("COMMODITY_NOMARKET") = COMMODITY_NOMARKET; scope().attr("COMMODITY_BUILTIN") = COMMODITY_BUILTIN; scope().attr("COMMODITY_WALKED") = COMMODITY_WALKED; scope().attr("COMMODITY_KNOWN") = COMMODITY_KNOWN; scope().attr("COMMODITY_PRIMARY") = COMMODITY_PRIMARY; class_< commodity_t, boost::noncopyable > ("Commodity", no_init) #if 1 .add_property("flags", &supports_flags<uint_least16_t>::flags, &supports_flags<uint_least16_t>::set_flags) .def("has_flags", &delegates_flags<uint_least16_t>::has_flags) .def("clear_flags", &delegates_flags<uint_least16_t>::clear_flags) .def("add_flags", &delegates_flags<uint_least16_t>::add_flags) .def("drop_flags", &delegates_flags<uint_least16_t>::drop_flags) #endif .add_static_property("decimal_comma_by_default", make_getter(&commodity_t::decimal_comma_by_default), make_setter(&commodity_t::decimal_comma_by_default)) .def("__str__", &commodity_t::symbol) .def("__unicode__", py_commodity_unicode) .def("__nonzero__", &commodity_t::operator bool) .def(self == self) .def("symbol_needs_quotes", &commodity_t::symbol_needs_quotes) .staticmethod("symbol_needs_quotes") .add_property("referent", make_function(py_commodity_referent, return_internal_reference<>())) .def("has_annotation", &commodity_t::has_annotation) .def("strip_annotations", py_strip_annotations_0, return_internal_reference<>()) .def("strip_annotations", py_strip_annotations_1, return_internal_reference<>()) .def("write_annotations", &commodity_t::write_annotations) .def("pool", &commodity_t::pool, return_internal_reference<>()) .add_property("base_symbol", &commodity_t::base_symbol) .add_property("symbol", &commodity_t::symbol) .add_property("name", &commodity_t::name, &commodity_t::set_name) .add_property("note", &commodity_t::note, &commodity_t::set_note) .add_property("precision", &commodity_t::precision, &commodity_t::set_precision) .add_property("smaller", &commodity_t::smaller, &commodity_t::set_smaller) .add_property("larger", &commodity_t::larger, &commodity_t::set_larger) .def("add_price", py_add_price_2) .def("add_price", py_add_price_3) .def("remove_price", &commodity_t::remove_price, with_custodian_and_ward<1, 3>()) .def("find_price", &commodity_t::find_price) .def("check_for_updated_price", &commodity_t::check_for_updated_price) .def("valid", &commodity_t::valid) ; class_< annotation_t > ("Annotation", no_init) #if 1 .add_property("flags", &supports_flags<>::flags, &supports_flags<>::set_flags) .def("has_flags", &supports_flags<>::has_flags) .def("clear_flags", &supports_flags<>::clear_flags) .def("add_flags", &supports_flags<>::add_flags) .def("drop_flags", &supports_flags<>::drop_flags) #endif .add_property("price", py_price, py_set_price) .add_property("date", make_getter(&annotation_t::date, return_value_policy<return_by_value>()), make_setter(&annotation_t::date, return_value_policy<return_by_value>())) .add_property("tag", make_getter(&annotation_t::tag, return_value_policy<return_by_value>()), make_setter(&annotation_t::tag, return_value_policy<return_by_value>())) .def("__nonzero__", &annotation_t::operator bool) .def(self == self) .def("valid", &annotation_t::valid) ; class_< keep_details_t > ("KeepDetails") .def(init<bool, bool, bool, bool>()) .add_property("keep_price", make_getter(&keep_details_t::keep_price), make_setter(&keep_details_t::keep_price)) .add_property("keep_date", make_getter(&keep_details_t::keep_date), make_setter(&keep_details_t::keep_date)) .add_property("keep_tag", make_getter(&keep_details_t::keep_tag), make_setter(&keep_details_t::keep_tag)) .add_property("only_actuals", make_getter(&keep_details_t::only_actuals), make_setter(&keep_details_t::only_actuals)) .def("keep_all", py_keep_all_0) .def("keep_all", py_keep_all_1) .def("keep_any", py_keep_any_0) .def("keep_any", py_keep_any_1) ; class_< annotated_commodity_t, bases<commodity_t>, annotated_commodity_t, boost::noncopyable > ("AnnotatedCommodity", no_init) .add_property("details", make_getter(&annotated_commodity_t::details), make_setter(&annotated_commodity_t::details)) .def(self == self) .def(self == other<commodity_t>()) .add_property("referent", make_function(py_annotated_commodity_referent, return_internal_reference<>())) .def("strip_annotations", py_strip_ann_annotations_0, return_internal_reference<>()) .def("strip_annotations", py_strip_ann_annotations_1, return_internal_reference<>()) .def("write_annotations", &annotated_commodity_t::write_annotations) ; } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_expr.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000004271�14411236400�0015544�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" namespace ledger { using namespace boost::python; namespace { value_t py_expr_call(expr_t& expr) { return expr.calc(); } } void export_expr() { class_< expr_t > ("Expr") .def(init<string>()) .def("__nonzero__", &expr_t::operator bool) .def("text", &expr_t::text) .def("set_text", &expr_t::set_text) //.def("parse", &expr_t::parse) .def("__call__", py_expr_call) .def("compile", &expr_t::compile) //.def("calc", &expr_t::calc) .def("is_constant", &expr_t::is_constant) //.def("constant_value", &expr_t::constant_value) ; } } // namespace ledger ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_format.cc�����������������������������������������������������������������������0000664�0000000�0000000�00000004274�14411236400�0016061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" namespace ledger { using namespace boost::python; #if 0 #define EXC_TRANSLATOR(type) \ void exc_translate_ ## type(const type& err) { \ PyErr_SetString(PyExc_ArithmeticError, err.what()); \ } //EXC_TRANSLATOR(format_error) #endif void export_format() { #if 0 class_< format_t > ("Format") ; #endif //register_optional_to_python<amount_t>(); //implicitly_convertible<string, amount_t>(); #if 0 #define EXC_TRANSLATE(type) \ register_exception_translator<type>(&exc_translate_ ## type); //EXC_TRANSLATE(format_error); #endif } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_item.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000014433�14411236400�0015525�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "scope.h" #include "mask.h" #include "item.h" namespace ledger { using namespace boost::python; namespace { bool py_has_tag_1s(item_t& item, const string& tag) { return item.has_tag(tag); } bool py_has_tag_1m(item_t& item, const mask_t& tag_mask) { return item.has_tag(tag_mask); } bool py_has_tag_2m(item_t& item, const mask_t& tag_mask, const boost::optional<mask_t>& value_mask) { return item.has_tag(tag_mask, value_mask); } boost::optional<value_t> py_get_tag_1s(item_t& item, const string& tag) { return item.get_tag(tag); } boost::optional<value_t> py_get_tag_1m(item_t& item, const mask_t& tag_mask) { return item.get_tag(tag_mask); } boost::optional<value_t> py_get_tag_2m(item_t& item, const mask_t& tag_mask, const boost::optional<mask_t>& value_mask) { return item.get_tag(tag_mask, value_mask); } std::string py_position_pathname(position_t const& pos) { return pos.pathname.native(); } void py_position_set_pathname(position_t& pos, string const& s) { pos.pathname = s; } } // unnamed namespace #if 0 #define EXC_TRANSLATOR(type) \ void exc_translate_ ## type(const type& err) { \ PyErr_SetString(PyExc_ArithmeticError, err.what()); \ } //EXC_TRANSLATOR(item_error) #endif void export_item() { class_< position_t > ("Position") .add_property("pathname", make_function(py_position_pathname), make_function(py_position_set_pathname)) .add_property("beg_pos", make_getter(&position_t::beg_pos), make_setter(&position_t::beg_pos)) .add_property("beg_line", make_getter(&position_t::beg_line), make_setter(&position_t::beg_line)) .add_property("end_pos", make_getter(&position_t::end_pos), make_setter(&position_t::end_pos)) .add_property("end_line", make_getter(&position_t::end_line), make_setter(&position_t::end_line)) ; scope().attr("ITEM_NORMAL") = ITEM_NORMAL; scope().attr("ITEM_GENERATED") = ITEM_GENERATED; scope().attr("ITEM_TEMP") = ITEM_TEMP; enum_< item_t::state_t > ("State") .value("Uncleared", item_t::UNCLEARED) .value("Cleared", item_t::CLEARED) .value("Pending", item_t::PENDING) ; #if 0 class_< item_t, bases<scope_t> > ("JournalItem", init<uint_least8_t>()) #else class_< item_t, noncopyable > ("JournalItem", no_init) #endif #if 1 .add_property("flags", &supports_flags<>::flags, &supports_flags<>::set_flags) .def("has_flags", &supports_flags<>::has_flags) .def("clear_flags", &supports_flags<>::clear_flags) .def("add_flags", &supports_flags<>::add_flags) .def("drop_flags", &supports_flags<>::drop_flags) #endif .add_property("note", make_getter(&item_t::note, return_value_policy<return_by_value>()), make_setter(&item_t::note, return_value_policy<return_by_value>())) .add_property("pos", make_getter(&item_t::pos, return_value_policy<return_by_value>()), make_setter(&item_t::pos, return_value_policy<return_by_value>())) .add_property("metadata", make_getter(&item_t::metadata, return_value_policy<return_by_value>()), make_setter(&item_t::metadata, return_value_policy<return_by_value>())) .def("copy_details", &item_t::copy_details) .def(self == self) .def(self != self) .def("has_tag", py_has_tag_1s) .def("has_tag", py_has_tag_1m) .def("has_tag", py_has_tag_2m) .def("get_tag", py_get_tag_1s) .def("get_tag", py_get_tag_1m) .def("get_tag", py_get_tag_2m) .def("tag", py_get_tag_1s) .def("tag", py_get_tag_1m) .def("tag", py_get_tag_2m) .def("set_tag", &item_t::set_tag) .def("parse_tags", &item_t::parse_tags) .def("append_note", &item_t::append_note) .add_static_property("use_aux_date", make_getter(&item_t::use_aux_date), make_setter(&item_t::use_aux_date)) .add_property("date", &item_t::date, make_setter(&item_t::_date)) .add_property("aux_date", &item_t::aux_date, make_setter(&item_t::_date_aux)) .add_property("state", &item_t::state, &item_t::set_state) .def("lookup", &item_t::lookup) .def("valid", &item_t::valid) ; register_optional_to_python<position_t>(); } } // namespace ledger �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_journal.cc����������������������������������������������������������������������0000664�0000000�0000000�00000024504�14411236400�0016241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "journal.h" #include "xact.h" #include "post.h" #include "chain.h" #include "filters.h" #include "iterators.h" #include "scope.h" #include "report.h" namespace ledger { using namespace boost::python; namespace { #if 0 account_t& py_account_master(journal_t& journal) { return *journal.master; } #endif long xacts_len(journal_t& journal) { return static_cast<long>(journal.xacts.size()); } #if 0 xact_t& xacts_getitem(journal_t& journal, long i) { static long last_index = 0; static journal_t * last_journal = NULL; static xacts_list::iterator elem; long len = static_cast<long>(journal.xacts.size()); if (labs(i) >= len) { PyErr_SetString(PyExc_IndexError, _("Index out of range")); throw_error_already_set(); } if (&journal == last_journal && i == last_index + 1) { last_index = i; return **++elem; } long x = i < 0 ? len + i : i; elem = journal.xacts.begin(); while (--x >= 0) elem++; last_journal = &journal; last_index = i; return **elem; } long accounts_len(account_t& account) { return static_cast<long>(account.accounts.size()); } account_t& accounts_getitem(account_t& account, long i) { static long last_index = 0; static account_t * last_account = NULL; static accounts_map::iterator elem; long len = static_cast<long>(account.accounts.size()); if (labs(i) >= len) { PyErr_SetString(PyExc_IndexError, _("Index out of range")); throw_error_already_set(); } if (&account == last_account && i == last_index + 1) { last_index = i; return *(*++elem).second; } long x = i < 0 ? len + i : i; elem = account.accounts.begin(); while (--x >= 0) elem++; last_account = &account; last_index = i; return *(*elem).second; } #endif account_t * py_find_account_1(journal_t& journal, const string& name) { return journal.find_account(name); } account_t * py_find_account_2(journal_t& journal, const string& name, const bool auto_create) { return journal.find_account(name, auto_create); } account_t * py_register_account(journal_t& journal, const string& name, post_t* post) { return journal.register_account(name, post, journal.master); } #if 0 std::size_t py_read(journal_t& journal, const string& pathname) { return journal.read(context_stack); } #endif struct collector_wrapper { journal_t& journal; report_t report; post_handler_ptr posts_collector; collector_wrapper(journal_t& _journal, report_t& base) : journal(_journal), report(base), posts_collector(new collect_posts) { TRACE_CTOR(collector_wrapper, "journal_t&, report_t&"); } ~collector_wrapper() { TRACE_DTOR(collector_wrapper); journal.clear_xdata(); } std::size_t length() const { return dynamic_cast<collect_posts *>(posts_collector.get())->length(); } std::vector<post_t *>::iterator begin() { return dynamic_cast<collect_posts *>(posts_collector.get())->begin(); } std::vector<post_t *>::iterator end() { return dynamic_cast<collect_posts *>(posts_collector.get())->end(); } }; shared_ptr<collector_wrapper> py_query(journal_t& journal, const string& query) { if (journal.has_xdata()) { PyErr_SetString(PyExc_RuntimeError, _("Cannot have more than one active journal query")); throw_error_already_set(); } report_t& current_report(downcast<report_t>(*scope_t::default_scope)); shared_ptr<collector_wrapper> coll(new collector_wrapper(journal, current_report)); unique_ptr<journal_t> save_journal(coll->report.session.journal.release()); coll->report.session.journal.reset(&coll->journal); try { strings_list remaining = process_arguments(split_arguments(query.c_str()), coll->report); coll->report.normalize_options("register"); value_t args; foreach (const string& arg, remaining) args.push_back(string_value(arg)); coll->report.parse_query_args(args, "@Journal.query"); coll->report.posts_report(coll->posts_collector); } catch (...) { coll->report.session.journal.release(); coll->report.session.journal.reset(save_journal.release()); throw; } coll->report.session.journal.release(); coll->report.session.journal.reset(save_journal.release()); return coll; } post_t * posts_getitem(collector_wrapper& collector, long i) { return dynamic_cast<collect_posts *>(collector.posts_collector.get()) ->posts[static_cast<std::size_t>(i)]; } } // unnamed namespace #define EXC_TRANSLATOR(type) \ void exc_translate_ ## type(const type& err) { \ PyErr_SetString(PyExc_RuntimeError, err.what()); \ } EXC_TRANSLATOR(parse_error) EXC_TRANSLATOR(error_count) void export_journal() { class_< item_handler<post_t>, shared_ptr<item_handler<post_t> >, boost::noncopyable >("PostHandler") ; class_< collector_wrapper, shared_ptr<collector_wrapper>, boost::noncopyable >("PostCollectorWrapper", no_init) .def("__len__", &collector_wrapper::length) .def("__getitem__", posts_getitem, return_internal_reference<>()) .def("__iter__", python::range<return_internal_reference<> > (&collector_wrapper::begin, &collector_wrapper::end)) ; class_< journal_t::fileinfo_t > ("FileInfo") .def(init<path>()) .add_property("filename", make_getter(&journal_t::fileinfo_t::filename), make_setter(&journal_t::fileinfo_t::filename)) .add_property("modtime", make_getter(&journal_t::fileinfo_t::modtime), make_setter(&journal_t::fileinfo_t::modtime)) .add_property("from_stream", make_getter(&journal_t::fileinfo_t::from_stream), make_setter(&journal_t::fileinfo_t::from_stream)) ; class_< journal_t, boost::noncopyable > ("Journal") #if 0 .def(init<path>()) .def(init<string>()) #endif .add_property("master", make_getter(&journal_t::master, return_internal_reference<1, with_custodian_and_ward_postcall<1, 0> >())) .add_property("bucket", make_getter(&journal_t::bucket, return_internal_reference<1, with_custodian_and_ward_postcall<1, 0> >()), make_setter(&journal_t::bucket)) .add_property("was_loaded", make_getter(&journal_t::was_loaded)) .def("add_account", &journal_t::add_account) .def("remove_account", &journal_t::remove_account) .def("find_account", py_find_account_1, return_internal_reference<1, with_custodian_and_ward_postcall<1, 0> >()) .def("find_account", py_find_account_2, return_internal_reference<1, with_custodian_and_ward_postcall<1, 0> >()) .def("find_account_re", &journal_t::find_account_re, return_internal_reference<1, with_custodian_and_ward_postcall<1, 0> >()) .def("register_account", py_register_account, return_internal_reference<1, with_custodian_and_ward_postcall<1, 0> >()) .def("expand_aliases", &journal_t::expand_aliases, return_internal_reference<1, with_custodian_and_ward_postcall<1, 0> >()) .def("add_xact", &journal_t::add_xact) .def("remove_xact", &journal_t::remove_xact) .def("__len__", xacts_len) #if 0 .def("__getitem__", xacts_getitem, return_internal_reference<1, with_custodian_and_ward_postcall<1, 0> >()) #endif .def("__iter__", python::range<return_internal_reference<> > (&journal_t::xacts_begin, &journal_t::xacts_end)) .def("xacts", python::range<return_internal_reference<> > (&journal_t::xacts_begin, &journal_t::xacts_end)) .def("auto_xacts", python::range<return_internal_reference<> > (&journal_t::auto_xacts_begin, &journal_t::auto_xacts_end)) .def("period_xacts", python::range<return_internal_reference<> > (&journal_t::period_xacts_begin, &journal_t::period_xacts_end)) .def("sources", python::range<return_internal_reference<> > (&journal_t::sources_begin, &journal_t::sources_end)) #if 0 .def("read", py_read) #endif .def("has_xdata", &journal_t::has_xdata) .def("clear_xdata", &journal_t::clear_xdata) .def("query", py_query) .def("valid", &journal_t::valid) ; #define EXC_TRANSLATE(type) \ register_exception_translator<type>(&exc_translate_ ## type); EXC_TRANSLATE(parse_error); EXC_TRANSLATE(error_count); } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_post.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000017046�14411236400�0015557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "post.h" #include "xact.h" namespace ledger { using namespace boost::python; namespace { bool py_has_tag_1s(post_t& post, const string& tag) { return post.has_tag(tag); } bool py_has_tag_1m(post_t& post, const mask_t& tag_mask) { return post.has_tag(tag_mask); } bool py_has_tag_2m(post_t& post, const mask_t& tag_mask, const boost::optional<mask_t>& value_mask) { return post.has_tag(tag_mask, value_mask); } boost::optional<value_t> py_get_tag_1s(post_t& post, const string& tag) { return post.get_tag(tag); } boost::optional<value_t> py_get_tag_1m(post_t& post, const mask_t& tag_mask) { return post.get_tag(tag_mask); } boost::optional<value_t> py_get_tag_2m(post_t& post, const mask_t& tag_mask, const boost::optional<mask_t>& value_mask) { return post.get_tag(tag_mask, value_mask); } post_t::xdata_t& py_xdata(post_t& post) { return post.xdata(); } account_t * py_reported_account(post_t& post) { return post.reported_account(); } } // unnamed namespace void export_post() { scope().attr("POST_EXT_RECEIVED") = POST_EXT_RECEIVED; scope().attr("POST_EXT_HANDLED") = POST_EXT_HANDLED; scope().attr("POST_EXT_DISPLAYED") = POST_EXT_DISPLAYED; scope().attr("POST_EXT_DIRECT_AMT") = POST_EXT_DIRECT_AMT; scope().attr("POST_EXT_SORT_CALC") = POST_EXT_SORT_CALC; scope().attr("POST_EXT_COMPOUND") = POST_EXT_COMPOUND; scope().attr("POST_EXT_VISITED") = POST_EXT_VISITED; scope().attr("POST_EXT_MATCHES") = POST_EXT_MATCHES; scope().attr("POST_EXT_CONSIDERED") = POST_EXT_CONSIDERED; class_< post_t::xdata_t > ("PostingXData") #if 1 .add_property("flags", &supports_flags<uint_least16_t>::flags, &supports_flags<uint_least16_t>::set_flags) .def("has_flags", &supports_flags<uint_least16_t>::has_flags) .def("clear_flags", &supports_flags<uint_least16_t>::clear_flags) .def("add_flags", &supports_flags<uint_least16_t>::add_flags) .def("drop_flags", &supports_flags<uint_least16_t>::drop_flags) #endif .add_property("visited_value", make_getter(&post_t::xdata_t::visited_value), make_setter(&post_t::xdata_t::visited_value)) .add_property("compound_value", make_getter(&post_t::xdata_t::compound_value), make_setter(&post_t::xdata_t::compound_value)) .add_property("total", make_getter(&post_t::xdata_t::total), make_setter(&post_t::xdata_t::total)) .add_property("count", make_getter(&post_t::xdata_t::count), make_setter(&post_t::xdata_t::count)) .add_property("date", make_getter(&post_t::xdata_t::date), make_setter(&post_t::xdata_t::date)) .add_property("datetime", make_getter(&post_t::xdata_t::datetime), make_setter(&post_t::xdata_t::datetime)) .add_property("account", make_getter(&post_t::xdata_t::account, return_internal_reference<>()), make_setter(&post_t::xdata_t::account, with_custodian_and_ward<1, 2>())) .add_property("sort_values", make_getter(&post_t::xdata_t::sort_values), make_setter(&post_t::xdata_t::sort_values)) ; scope().attr("POST_VIRTUAL") = POST_VIRTUAL; scope().attr("POST_MUST_BALANCE") = POST_MUST_BALANCE; scope().attr("POST_CALCULATED") = POST_CALCULATED; scope().attr("POST_COST_CALCULATED") = POST_COST_CALCULATED; class_< post_t, bases<item_t> > ("Posting") //.def(init<account_t *>()) .def("id", &post_t::id) .def("seq", &post_t::seq) .add_property("xact", make_getter(&post_t::xact, return_internal_reference<>()), make_setter(&post_t::xact, with_custodian_and_ward<1, 2>())) .add_property("account", make_getter(&post_t::account, return_internal_reference<>()), make_setter(&post_t::account, with_custodian_and_ward<1, 2>())) .add_property("amount", make_getter(&post_t::amount), make_setter(&post_t::amount)) .add_property("cost", make_getter(&post_t::cost, return_value_policy<return_by_value>()), make_setter(&post_t::cost, return_value_policy<return_by_value>())) .add_property("given_cost", make_getter(&post_t::given_cost, return_value_policy<return_by_value>()), make_setter(&post_t::given_cost, return_value_policy<return_by_value>())) .add_property("assigned_amount", make_getter(&post_t::assigned_amount, return_value_policy<return_by_value>()), make_setter(&post_t::assigned_amount, return_value_policy<return_by_value>())) .def("has_tag", py_has_tag_1s) .def("has_tag", py_has_tag_1m) .def("has_tag", py_has_tag_2m) .def("get_tag", py_get_tag_1s) .def("get_tag", py_get_tag_1m) .def("get_tag", py_get_tag_2m) .add_property("date", &post_t::date) .add_property("aux_date", &post_t::aux_date) .def("must_balance", &post_t::must_balance) .def("lookup", &post_t::lookup) .def("valid", &post_t::valid) .def("has_xdata", &post_t::has_xdata) .def("clear_xdata", &post_t::clear_xdata) .def("xdata", py_xdata, return_internal_reference<>()) //.def("add_to_value", &post_t::add_to_value) .def("set_reported_account", &post_t::set_reported_account) .def("reported_account", py_reported_account, return_internal_reference<>()) ; } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_session.cc����������������������������������������������������������������������0000664�0000000�0000000�00000006330�14411236400�0016247�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "error.h" #include "session.h" namespace ledger { using namespace boost::python; namespace { journal_t * py_read_journal(const string& pathname) { return python_session->read_journal(path(pathname)); } journal_t * py_read_journal_from_string(const string& data) { return python_session->read_journal_from_string(data); } PyObject* py_error_context(const session_t& session) { return str_to_py_unicode(error_context()); } void py_close_journal_files() { python_session->close_journal_files(); } } void export_session() { class_< session_t, boost::noncopyable > ("Session") .def("read_journal", &session_t::read_journal, return_internal_reference<>()) .def("read_journal_from_string", &session_t::read_journal_from_string, return_internal_reference<>()) .def("read_journal_files", &session_t::read_journal_files, return_internal_reference<>()) .def("close_journal_files", &session_t::close_journal_files) .def("journal", &session_t::get_journal, return_internal_reference<>()) .def("error_context", &py_error_context) ; scope().attr("session") = object(ptr(static_cast<session_t *>(python_session.get()))); scope().attr("close_journal_files") = python::make_function(&py_close_journal_files); scope().attr("read_journal") = python::make_function(&py_read_journal, return_internal_reference<>()); scope().attr("read_journal_from_string") = python::make_function(&py_read_journal_from_string, return_internal_reference<>()); } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_times.cc������������������������������������������������������������������������0000664�0000000�0000000�00000016774�14411236400�0015722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include <datetime.h> #include "pyinterp.h" #include "pyutils.h" #include "times.h" // jww (2007-05-04): Convert time duration objects to PyDelta namespace ledger { using namespace boost::python; struct date_to_python { static PyObject* convert(const date_t& dte) { PyDateTime_IMPORT; return PyDate_FromDate(dte.year(), dte.month(), dte.day()); } }; struct date_from_python { static void* convertible(PyObject* obj_ptr) { PyDateTime_IMPORT; if (PyDate_Check(obj_ptr)) return obj_ptr; return 0; } static void construct(PyObject * obj_ptr, converter::rvalue_from_python_stage1_data * data) { PyDateTime_IMPORT; int year = PyDateTime_GET_YEAR(obj_ptr); date::year_type y = gregorian::greg_year(static_cast<unsigned short>(year)); date::month_type m = static_cast<date::month_type>(PyDateTime_GET_MONTH(obj_ptr)); date::day_type d = static_cast<date::day_type>(PyDateTime_GET_DAY(obj_ptr)); date_t * dte = new date_t(y, m, d); data->convertible = (void *) dte; } }; typedef register_python_conversion<date_t, date_to_python, date_from_python> date_python_conversion; struct datetime_to_python { static PyObject* convert(const datetime_t& moment) { PyDateTime_IMPORT; date_t dte = moment.date(); datetime_t::time_duration_type tod = moment.time_of_day(); return PyDateTime_FromDateAndTime (static_cast<int>(dte.year()), static_cast<int>(dte.month()), static_cast<int>(dte.day()), static_cast<int>(tod.hours()), static_cast<int>(tod.minutes()), static_cast<int>(tod.seconds()), static_cast<int>(tod.total_microseconds() % 1000000)); } }; struct datetime_from_python { static void* convertible(PyObject* obj_ptr) { PyDateTime_IMPORT; if (PyDateTime_Check(obj_ptr)) return obj_ptr; return 0; } static void construct(PyObject * obj_ptr, converter::rvalue_from_python_stage1_data * data) { PyDateTime_IMPORT; int year = PyDateTime_GET_YEAR(obj_ptr); date::year_type y = gregorian::greg_year(static_cast<unsigned short>(year)); date::month_type m = static_cast<date::month_type>(PyDateTime_GET_MONTH(obj_ptr)); date::day_type d = static_cast<date::day_type>(PyDateTime_GET_DAY(obj_ptr)); datetime_t::time_duration_type::hour_type h = static_cast<datetime_t::time_duration_type::hour_type> (PyDateTime_DATE_GET_HOUR(obj_ptr)); datetime_t::time_duration_type::min_type min = static_cast<datetime_t::time_duration_type::min_type> (PyDateTime_DATE_GET_MINUTE(obj_ptr)); datetime_t::time_duration_type::sec_type s = static_cast<datetime_t::time_duration_type::sec_type> (PyDateTime_DATE_GET_SECOND(obj_ptr)); datetime_t::time_duration_type::fractional_seconds_type ms = static_cast<datetime_t::time_duration_type::fractional_seconds_type> (PyDateTime_DATE_GET_MICROSECOND(obj_ptr)) * 1000000; datetime_t * moment = new datetime_t(date_t(y, m, d), datetime_t::time_duration_type(h, min, s, ms)); data->convertible = (void *) moment; } }; typedef register_python_conversion<datetime_t, datetime_to_python, datetime_from_python> datetime_python_conversion; /* Convert time_duration to/from python */ struct duration_to_python { static int get_usecs(boost::posix_time::time_duration const& d) { static int64_t resolution = boost::posix_time::time_duration::ticks_per_second(); int64_t fracsecs = d.fractional_seconds(); if (resolution > 1000000) return static_cast<int>(fracsecs / (resolution / 1000000)); else return static_cast<int>(fracsecs * (1000000 / resolution)); } static PyObject * convert(posix_time::time_duration d) { int days = d.hours() / 24; if (days < 0) days --; int seconds = d.total_seconds() - days*(24*3600); int usecs = get_usecs(d); if (days < 0) usecs = 1000000-1 - usecs; return PyDelta_FromDSU(days, seconds, usecs); } }; /* Should support the negative values, but not the special boost time durations */ struct duration_from_python { static void* convertible(PyObject * obj_ptr) { if ( ! PyDelta_Check(obj_ptr)) return 0; return obj_ptr; } static void construct(PyObject* obj_ptr, python::converter::rvalue_from_python_stage1_data * data) { PyDateTime_Delta const* pydelta = reinterpret_cast<PyDateTime_Delta*>(obj_ptr); int days = pydelta->days; bool is_negative = (days < 0); if (is_negative) days = -days; // Create time duration object posix_time::time_duration duration = (posix_time::hours(24) * days + posix_time::seconds(pydelta->seconds) + posix_time::microseconds(pydelta->microseconds)); if (is_negative) duration = duration.invert_sign(); void * storage = reinterpret_cast<converter::rvalue_from_python_storage <posix_time::time_duration> *> (data)->storage.bytes; new (storage) posix_time::time_duration(duration); data->convertible = storage; } }; typedef register_python_conversion<time_duration_t, duration_to_python, duration_from_python> duration_python_conversion; datetime_t py_parse_datetime(const string& str) { return parse_datetime(str); } date_t py_parse_date(const string& str) { return parse_date(str); } void export_times() { datetime_python_conversion(); date_python_conversion(); duration_python_conversion(); register_optional_to_python<datetime_t>(); register_optional_to_python<date_t>(); scope().attr("parse_datetime") = &py_parse_datetime; scope().attr("parse_date") = &py_parse_date; scope().attr("times_initialize") = ×_initialize; scope().attr("times_shutdown") = ×_shutdown; #if 0 class_< interval_t > ("Interval") ; #endif } } // namespace ledger ����ledger-3.3.2/src/py_utils.cc������������������������������������������������������������������������0000664�0000000�0000000�00000015545�14411236400�0015734�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" namespace ledger { using namespace boost::python; struct bool_to_python { static PyObject * convert(const bool truth) { if (truth) Py_RETURN_TRUE; else Py_RETURN_FALSE; } }; struct bool_from_python { static void* convertible(PyObject* obj_ptr) { if (!PyBool_Check(obj_ptr)) return 0; return obj_ptr; } static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) { void * storage = ((converter::rvalue_from_python_storage<bool>*) data)->storage.bytes; if (obj_ptr == Py_True) new (storage) bool(true); else new (storage) bool(false); data->convertible = storage; } }; typedef register_python_conversion<bool, bool_to_python, bool_from_python> bool_python_conversion; struct string_to_python { static PyObject* convert(const string& str) { // Return bytes, not characters; see __unicode__ methods for that return incref(object(static_cast<const std::string&>(str)).ptr()); } }; struct string_from_python { static void* convertible(PyObject* obj_ptr) { if (!PyUnicode_Check(obj_ptr)) return 0; return obj_ptr; } static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) { VERIFY(PyUnicode_Check(obj_ptr)); #if PY_MINOR_VERSION < 12 if (PyUnicode_READY(obj_ptr)) return; #endif const Py_UNICODE* value; Py_ssize_t size; string str; #if PY_MINOR_VERSION >= 3 size = PyUnicode_GET_LENGTH(obj_ptr); switch (PyUnicode_KIND(obj_ptr)) { case PyUnicode_1BYTE_KIND: { Py_UCS1* value = PyUnicode_1BYTE_DATA(obj_ptr); if (value == 0) throw_error_already_set(); utf8::unchecked::utf16to8(value, value + size, std::back_inserter(str)); } break; #if PY_MINOR_VERSION < 12 && Py_UNICODE_SIZE == 2 case PyUnicode_WCHAR_KIND: #endif case PyUnicode_2BYTE_KIND: { Py_UCS2* value = PyUnicode_2BYTE_DATA(obj_ptr); if (value == 0) throw_error_already_set(); utf8::unchecked::utf16to8(value, value + size, std::back_inserter(str)); } break; #if PY_MINOR_VERSION < 12 && Py_UNICODE_SIZE == 4 case PyUnicode_WCHAR_KIND: #endif case PyUnicode_4BYTE_KIND: { Py_UCS4* value = PyUnicode_4BYTE_DATA(obj_ptr); if (value == 0) throw_error_already_set(); utf8::unchecked::utf32to8(value, value + size, std::back_inserter(str)); } break; default: assert("PyUnicode_KIND returned an unexpected kind" == NULL); } #else size = PyUnicode_GET_SIZE(obj_ptr); value = PyUnicode_AS_UNICODE(obj_ptr); #if Py_UNICODE_SIZE == 2 // UTF-16 utf8::unchecked::utf16to8(value, value + size, std::back_inserter(str)); #elif Py_UNICODE_SIZE == 4 // UTF-32 utf8::unchecked::utf32to8(value, value + size, std::back_inserter(str)); #else assert("Py_UNICODE has an unexpected size" == NULL); #endif #endif void* storage = reinterpret_cast<converter::rvalue_from_python_storage<string> *> (data)->storage.bytes; new (storage) string(str); data->convertible = storage; } }; typedef register_python_conversion<string, string_to_python, string_from_python> string_python_conversion; void export_utils() { class_< supports_flags<uint_least8_t> > ("SupportFlags8") .def(init<supports_flags<uint_least8_t> >()) .def(init<uint_least8_t>()) .add_property("flags", &supports_flags<uint_least8_t>::flags, &supports_flags<uint_least8_t>::set_flags) .def("has_flags", &supports_flags<uint_least8_t>::has_flags) .def("clear_flags", &supports_flags<uint_least8_t>::clear_flags) .def("add_flags", &supports_flags<uint_least8_t>::add_flags) .def("drop_flags", &supports_flags<uint_least8_t>::drop_flags) ; class_< supports_flags<uint_least16_t> > ("SupportFlags16") .def(init<supports_flags<uint_least16_t> >()) .def(init<uint_least16_t>()) .add_property("flags", &supports_flags<uint_least16_t>::flags, &supports_flags<uint_least16_t>::set_flags) .def("has_flags", &supports_flags<uint_least16_t>::has_flags) .def("clear_flags", &supports_flags<uint_least16_t>::clear_flags) .def("add_flags", &supports_flags<uint_least16_t>::add_flags) .def("drop_flags", &supports_flags<uint_least16_t>::drop_flags) ; #if 0 class_< basic_flags_t<uint_least8_t>, bases<supports_flags<uint_least8_t> > > ("BasicFlags8") .def(init<uint_least8_t>()) .def("plus_flags", &basic_flags_t<uint_least8_t>::plus_flags) .def("minus_flags", &basic_flags_t<uint_least8_t>::minus_flags) ; #endif class_< delegates_flags<uint_least16_t>, boost::noncopyable > ("DelegatesFlags16", no_init) .add_property("flags", &delegates_flags<uint_least16_t>::flags, &delegates_flags<uint_least16_t>::set_flags) .def("has_flags", &delegates_flags<uint_least16_t>::has_flags) .def("clear_flags", &delegates_flags<uint_least16_t>::clear_flags) .def("add_flags", &delegates_flags<uint_least16_t>::add_flags) .def("drop_flags", &delegates_flags<uint_least16_t>::drop_flags) ; bool_python_conversion(); string_python_conversion(); } } // namespace ledger �����������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_value.cc������������������������������������������������������������������������0000664�0000000�0000000�00000027462�14411236400�0015711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "commodity.h" #include "annotate.h" namespace ledger { using namespace boost::python; BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(value_overloads, value, 0, 2) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(exchange_commodities_overloads, exchange_commodities, 1, 2) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(set_string_overloads, set_string, 0, 2) namespace { boost::optional<value_t> py_value_0(const value_t& value) { return value.value(CURRENT_TIME()); } boost::optional<value_t> py_value_1(const value_t& value, const commodity_t * in_terms_of) { return value.value(CURRENT_TIME(), in_terms_of); } boost::optional<value_t> py_value_2(const value_t& value, const commodity_t * in_terms_of, const datetime_t& moment) { return value.value(moment, in_terms_of); } boost::optional<value_t> py_value_2d(const value_t& value, const commodity_t * in_terms_of, const date_t& moment) { return value.value(datetime_t(moment), in_terms_of); } PyObject * py_base_type(value_t& value) { if (value.is_boolean()) { return (PyObject *)&PyBool_Type; } else if (value.is_long()) { return (PyObject *)&PyLong_Type; } else if (value.is_string()) { return (PyObject *)&PyUnicode_Type; } else { object typeobj(object(value).attr("__class__")); return typeobj.ptr(); } } string py_dump(const value_t& value) { std::ostringstream buf; value.dump(buf); return buf.str(); } void py_set_string(value_t& value, const string& str) { return value.set_string(str); } annotation_t& py_value_annotation(value_t& value) { return value.annotation(); } value_t py_strip_annotations_0(value_t& value) { return value.strip_annotations(keep_details_t()); } value_t py_strip_annotations_1(value_t& value, const keep_details_t& keep) { return value.strip_annotations(keep); } PyObject * py_value_unicode(value_t& value) { return str_to_py_unicode(value.to_string()); } } // unnamed namespace #define EXC_TRANSLATOR(type) \ void exc_translate_ ## type(const type& err) { \ PyErr_SetString(PyExc_ArithmeticError, err.what()); \ } EXC_TRANSLATOR(value_error) void export_value() { enum_< value_t::type_t >("ValueType") .value("Void", value_t::VOID) .value("Boolean", value_t::BOOLEAN) .value("DateTime", value_t::DATETIME) .value("Date", value_t::DATE) .value("Integer", value_t::INTEGER) .value("Amount", value_t::AMOUNT) .value("Balance", value_t::BALANCE) .value("String", value_t::STRING) .value("Sequence", value_t::SEQUENCE) .value("Scope", value_t::SCOPE) ; class_< value_t > ("Value") .def("initialize", &value_t::initialize) .staticmethod("initialize") .def("shutdown", &value_t::shutdown) .staticmethod("shutdown") .def(init<bool>()) .def(init<datetime_t>()) .def(init<date_t>()) .def(init<long>()) .def(init<double>()) .def(init<amount_t>()) .def(init<balance_t>()) .def(init<mask_t>()) .def(init<std::string>()) // jww (2009-11-02): Need to support conversion of value_t::sequence_t //.def(init<value_t::sequence_t>()) .def(init<value_t>()) .def("is_equal_to", &value_t::is_equal_to) .def("is_less_than", &value_t::is_less_than) .def("is_greater_than", &value_t::is_greater_than) .def(self == self) .def(self == long()) .def(long() == self) .def(self == other<amount_t>()) .def(other<amount_t>() == self) .def(self == other<balance_t>()) .def(other<balance_t>() == self) .def(self != self) .def(self != long()) .def(long() != self) .def(self != other<amount_t>()) .def(other<amount_t>() != self) .def(self != other<balance_t>()) .def(other<balance_t>() != self) .def(! self) .def(self < self) .def(self < long()) .def(long() < self) .def(self < other<amount_t>()) .def(other<amount_t>() < self) .def(self <= self) .def(self <= long()) .def(long() <= self) .def(self <= other<amount_t>()) .def(other<amount_t>() <= self) .def(self > self) .def(self > long()) .def(long() > self) .def(self > other<amount_t>()) .def(other<amount_t>() > self) .def(self >= self) .def(self >= long()) .def(long() >= self) .def(self >= other<amount_t>()) .def(other<amount_t>() >= self) .def(self += self) .def(self += long()) .def(self += other<amount_t>()) .def(self += other<balance_t>()) .def(self + self) .def(self + long()) .def(long() + self) .def(self + other<amount_t>()) .def(other<amount_t>() + self) .def(self + other<balance_t>()) .def(self -= self) .def(self -= long()) .def(self -= other<amount_t>()) .def(self -= other<balance_t>()) .def(self - self) .def(self - long()) .def(long() - self) .def(self - other<amount_t>()) .def(other<amount_t>() - self) .def(self - other<balance_t>()) .def(self *= self) .def(self *= long()) .def(self *= other<amount_t>()) .def(self * self) .def(self * long()) .def(long() * self) .def(self * other<amount_t>()) .def(other<amount_t>() * self) .def(self /= self) .def(self /= long()) .def(self /= other<amount_t>()) .def(self / self) .def(self / long()) .def(long() / self) .def(self / other<amount_t>()) .def(other<amount_t>() / self) .def("negated", &value_t::negated) .def("in_place_negate", &value_t::in_place_negate) .def("in_place_not", &value_t::in_place_not) .def(- self) .def("abs", &value_t::abs) .def("__abs__", &value_t::abs) .def("rounded", &value_t::rounded) .def("in_place_round", &value_t::in_place_round) .def("truncated", &value_t::truncated) .def("in_place_truncate", &value_t::in_place_truncate) .def("floored", &value_t::floored) .def("in_place_floor", &value_t::in_place_floor) .def("unrounded", &value_t::unrounded) .def("in_place_unround", &value_t::in_place_unround) .def("reduced", &value_t::reduced) .def("in_place_reduce", &value_t::in_place_reduce) .def("unreduced", &value_t::unreduced) .def("in_place_unreduce", &value_t::in_place_unreduce) .def("value", py_value_0) .def("value", py_value_1, args("in_terms_of")) .def("value", py_value_2, args("in_terms_of", "moment")) .def("value", py_value_2d, args("in_terms_of", "moment")) //.def("value", &value_t::value, value_overloads()) .def("exchange_commodities", &value_t::exchange_commodities, exchange_commodities_overloads()) .def("__nonzero__", &value_t::is_nonzero) .def("is_nonzero", &value_t::is_nonzero) .def("is_realzero", &value_t::is_realzero) .def("is_zero", &value_t::is_zero) .def("is_null", &value_t::is_null) .def("type", &value_t::type) .def("is_type", &value_t::is_type) .def("is_boolean", &value_t::is_boolean) .def("set_boolean", &value_t::set_boolean) .def("is_datetime", &value_t::is_datetime) .def("set_datetime", &value_t::set_datetime) .def("is_date", &value_t::is_date) .def("set_date", &value_t::set_date) .def("is_long", &value_t::is_long) .def("set_long", &value_t::set_long) .def("is_amount", &value_t::is_amount) .def("is_amount", &value_t::is_amount) .def("is_balance", &value_t::is_balance) .def("is_balance", &value_t::is_balance) .def("is_string", &value_t::is_string) .def("set_string", py_set_string) .def("is_mask", &value_t::is_mask) .def("is_mask", &value_t::is_mask) .def("is_sequence", &value_t::is_sequence) .def("set_sequence", &value_t::set_sequence) .def("to_boolean", &value_t::to_boolean) .def("to_long", &value_t::to_long) .def("__int__", &value_t::to_long) .def("to_datetime", &value_t::to_datetime) .def("to_date", &value_t::to_date) .def("to_amount", &value_t::to_amount) .def("to_balance", &value_t::to_balance) .def("__str__", &value_t::to_string) .def("__unicode__", py_value_unicode) .def("to_string", &value_t::to_string) .def("to_mask", &value_t::to_mask) .def("to_sequence", &value_t::to_sequence) .def("__repr__", py_dump) .def("casted", &value_t::casted) .def("in_place_cast", &value_t::in_place_cast) .def("simplified", &value_t::simplified) .def("in_place_simplify", &value_t::in_place_simplify) .def("number", &value_t::number) .def("annotate", &value_t::annotate) .def("has_annotation", &value_t::has_annotation) .add_property("annotation", make_function(py_value_annotation, return_internal_reference<>())) .def("strip_annotations", py_strip_annotations_0) .def("strip_annotations", py_strip_annotations_1) #if 0 .def("__getitem__", &value_t::operator[]) #endif .def("push_back", &value_t::push_back) .def("pop_back", &value_t::pop_back) .def("size", &value_t::size) .def("label", &value_t::label) .def("valid", &value_t::valid) .def("basetype", py_base_type) ; #if 0 // jww (2010-06-10): This is not working since I switched sequence_t to // ptr_deque<value_t>. class_< value_t::sequence_t > ("ValueSequence") .def(vector_indexing_suite< value_t::sequence_t, true >()); ; #endif scope().attr("NULL_VALUE") = NULL_VALUE; scope().attr("string_value") = &string_value; scope().attr("mask_value") = &mask_value; scope().attr("value_context") = &value_context; register_optional_to_python<value_t>(); implicitly_convertible<bool, value_t>(); implicitly_convertible<long, value_t>(); implicitly_convertible<string, value_t>(); implicitly_convertible<amount_t, value_t>(); implicitly_convertible<balance_t, value_t>(); implicitly_convertible<mask_t, value_t>(); implicitly_convertible<date_t, value_t>(); implicitly_convertible<datetime_t, value_t>(); #define EXC_TRANSLATE(type) \ register_exception_translator<type>(&exc_translate_ ## type); EXC_TRANSLATE(value_error); } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/py_xact.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000011737�14411236400�0015532�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "xact.h" #include "post.h" namespace ledger { using namespace boost::python; namespace { long posts_len(xact_base_t& xact) { return static_cast<long>(xact.posts.size()); } post_t& posts_getitem(xact_base_t& xact, long i) { static long last_index = 0; static xact_base_t * last_xact = NULL; static posts_list::iterator elem; long len = static_cast<long>(xact.posts.size()); if (labs(i) >= len) { PyErr_SetString(PyExc_IndexError, _("Index out of range")); throw_error_already_set(); } if (&xact == last_xact && i == last_index + 1) { last_index = i; return **++elem; } long x = i < 0 ? len + i : i; elem = xact.posts.begin(); while (--x >= 0) elem++; last_xact = &xact; last_index = i; return **elem; } string py_xact_to_string(xact_t&) { // jww (2012-03-01): TODO return empty_string; } } // unnamed namespace using namespace boost::python; void export_xact() { class_< xact_base_t, bases<item_t>, noncopyable > ("TransactionBase", no_init) .add_property("journal", make_getter(&xact_base_t::journal, return_internal_reference<>()), make_setter(&xact_base_t::journal, with_custodian_and_ward<1, 2>())) .def("__len__", posts_len) .def("__getitem__", posts_getitem, return_internal_reference<>()) .def("add_post", &xact_base_t::add_post, with_custodian_and_ward<1, 2>()) .def("remove_post", &xact_base_t::remove_post) .def("finalize", &xact_base_t::finalize) .def("__iter__", python::range<return_internal_reference<> > (&xact_t::posts_begin, &xact_t::posts_end)) .def("posts", python::range<return_internal_reference<> > (&xact_t::posts_begin, &xact_t::posts_end)) .def("valid", &xact_base_t::valid) ; class_< xact_t, bases<xact_base_t> > ("Transaction") .def("id", &xact_t::id) .def("seq", &xact_t::seq) .def("__str__", py_xact_to_string) .add_property("code", make_getter(&xact_t::code, return_value_policy<return_by_value>()), make_setter(&xact_t::code, return_value_policy<return_by_value>())) .add_property("payee", make_getter(&xact_t::payee), make_setter(&xact_t::payee)) .def("add_post", &xact_t::add_post, with_custodian_and_ward<1, 2>()) .def("magnitude", &xact_t::magnitude) .def("lookup", &xact_t::lookup) .def("has_xdata", &xact_t::has_xdata) .def("clear_xdata", &xact_t::clear_xdata) .def("valid", &xact_t::valid) ; class_< auto_xact_t, bases<xact_base_t> > ("AutomatedTransaction") .def(init<predicate_t>()) .add_property("predicate", make_getter(&auto_xact_t::predicate), make_setter(&auto_xact_t::predicate)) .def("extend_xact", &auto_xact_t::extend_xact) ; class_< period_xact_t, bases<xact_base_t> > ("PeriodicTransaction") .def(init<string>()) .add_property("period", make_getter(&period_xact_t::period), make_setter(&period_xact_t::period)) .add_property("period_string", make_getter(&period_xact_t::period_string), make_setter(&period_xact_t::period_string)) ; register_optional_to_python<std::string>(); } } // namespace ledger ���������������������������������ledger-3.3.2/src/pyinterp.cc������������������������������������������������������������������������0000664�0000000�0000000�00000037426�14411236400�0015740�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" #include "pyutils.h" #include "account.h" #include "report.h" #include "xact.h" #include "post.h" namespace ledger { using namespace python; shared_ptr<python_interpreter_t> python_session; char * argv0; void export_account(); void export_amount(); void export_balance(); void export_commodity(); void export_expr(); void export_format(); void export_item(); void export_session(); void export_journal(); void export_post(); void export_times(); void export_utils(); void export_value(); void export_xact(); extern "C" PyObject* PyInit_ledger(); void initialize_for_python() { export_times(); export_utils(); export_commodity(); export_amount(); export_value(); export_account(); export_balance(); export_expr(); export_format(); export_item(); export_post(); export_xact(); export_session(); export_journal(); if (! scope_t::default_scope) { python_session.reset(new ledger::python_interpreter_t); shared_ptr<session_t> session_ptr = python_session; scope_t::default_scope = new report_t(*session_ptr); } } struct python_run { object result; python_run(python_interpreter_t * interpreter, const string& str, int input_mode) : result (handle<> (borrowed (PyRun_String(str.c_str(), input_mode, interpreter->main_module->module_globals.ptr(), interpreter->main_module->module_globals.ptr())))) {} operator object() { return result; } }; python_module_t::python_module_t(const string& name) : scope_t(), module_name(name), module_globals() { import_module(name); } python_module_t::python_module_t(const string& name, python::object obj) : scope_t(), module_name(name), module_globals() { module_object = obj; module_globals = extract<dict>(module_object.attr("__dict__")); } void python_module_t::import_module(const string& name, bool import_direct) { object mod = python::import(name.c_str()); if (! mod) throw_(std::runtime_error, _f("Module import failed (couldn't find %1%)") % name); dict globals = extract<dict>(mod.attr("__dict__")); if (! globals) throw_(std::runtime_error, _f("Module import failed (couldn't find %1%)") % name); if (! import_direct) { module_object = mod; module_globals = globals; } else { // Import all top-level entries directly into the namespace module_globals.update(mod.attr("__dict__")); } } void python_interpreter_t::initialize() { if (is_initialized) return; TRACE_START(python_init, 1, "Initialized Python"); try { DEBUG("python.interp", "Initializing Python"); // Unbuffer stdio to avoid python output getting stuck in buffer when // stdout is not a TTY. Normally buffers are flushed by Py_Finalize but // Boost has a long-standing issue preventing proper shutdown of the // interpreter with Py_Finalize when embedded. Py_UnbufferedStdioFlag = 1; // PyImport_AppendInittab docs: "This should be called before Py_Initialize()". PyImport_AppendInittab((const char*)"ledger", PyInit_ledger); Py_Initialize(); assert(Py_IsInitialized()); hack_system_paths(); main_module = import_module("__main__"); PyImport_ImportModule("ledger"); is_initialized = true; } catch (const error_already_set&) { PyErr_Print(); throw_(std::runtime_error, _("Python failed to initialize")); } TRACE_FINISH(python_init, 1); } void python_interpreter_t::hack_system_paths() { // Hack ledger.__path__ so it points to a real location python::object sys_module = python::import("sys"); python::object sys_dict = sys_module.attr("__dict__"); python::list paths(sys_dict["path"]); #if DEBUG_ON bool path_initialized = false; #endif int n = python::extract<int>(paths.attr("__len__")()); for (int i = 0; i < n; i++) { python::extract<std::string> str(paths[i]); path pathname(str()); DEBUG("python.interp", "sys.path = " << pathname); if (exists(pathname / "ledger" / "__init__.py")) { if (python::object module_ledger = python::import("ledger")) { DEBUG("python.interp", "Setting ledger.__path__ = " << (pathname / "ledger")); python::object ledger_dict = module_ledger.attr("__dict__"); python::list temp_list; temp_list.append((pathname / "ledger").string()); ledger_dict["__path__"] = temp_list; } else { throw_(std::runtime_error, _("Python failed to initialize (couldn't find ledger)")); } #if DEBUG_ON path_initialized = true; #endif break; } } #if DEBUG_ON if (! path_initialized) DEBUG("python.init", "Ledger failed to find 'ledger/__init__.py' on the PYTHONPATH"); #endif } object python_interpreter_t::import_option(const string& str) { if (! is_initialized) initialize(); python::object sys_module = python::import("sys"); python::object sys_dict = sys_module.attr("__dict__"); path file(str); string name(str); python::list paths(sys_dict["path"]); if (contains(str, ".py")) { path& cwd(parsing_context.get_current().current_directory); path parent(filesystem::absolute(file, cwd).parent_path()); DEBUG("python.interp", "Adding " << parent << " to PYTHONPATH"); paths.insert(0, parent.string()); sys_dict["path"] = paths; name = file.stem().string(); } try { if (contains(str, ".py")) main_module->import_module(name, true); else import_module(str); } catch (const error_already_set&) { PyErr_Print(); throw_(std::runtime_error, _f("Python failed to import: %1%") % str); } catch (...) { throw; } return object(); } object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode) { bool first = true; string buffer; buffer.reserve(4096); while (! in.eof()) { char buf[256]; in.getline(buf, 255); if (buf[0] == '!') break; if (first) first = false; else buffer += "\n"; buffer += buf; } if (! is_initialized) initialize(); try { int input_mode = -1; switch (mode) { case PY_EVAL_EXPR: input_mode = Py_eval_input; break; case PY_EVAL_STMT: input_mode = Py_single_input; break; case PY_EVAL_MULTI: input_mode = Py_file_input; break; } return python_run(this, buffer, input_mode); } catch (const error_already_set&) { PyErr_Print(); throw_(std::runtime_error, _("Failed to evaluate Python code")); } return object(); } object python_interpreter_t::eval(const string& str, py_eval_mode_t mode) { if (! is_initialized) initialize(); try { int input_mode = -1; switch (mode) { case PY_EVAL_EXPR: input_mode = Py_eval_input; break; case PY_EVAL_STMT: input_mode = Py_single_input; break; case PY_EVAL_MULTI: input_mode = Py_file_input; break; } return python_run(this, str, input_mode); } catch (const error_already_set&) { PyErr_Print(); throw_(std::runtime_error, _("Failed to evaluate Python code")); } return object(); } value_t python_interpreter_t::python_command(call_scope_t& args) { if (! is_initialized) initialize(); wchar_t ** argv = new wchar_t *[args.size() + 1]; std::size_t len = std::strlen(argv0) + 1; argv[0] = new wchar_t[len]; mbstowcs(argv[0], argv0, len); for (std::size_t i = 0; i < args.size(); i++) { string arg = args.get<string>(i); std::size_t len = arg.length() + 1; argv[i + 1] = new wchar_t[len]; mbstowcs(argv[i + 1], arg.c_str(), len); } int status = 1; try { status = Py_Main(static_cast<int>(args.size()) + 1, argv); } catch (const error_already_set&) { PyErr_Print(); throw_(std::runtime_error, _("Failed to execute Python module")); } catch (...) { for (std::size_t i = 0; i < args.size() + 1; i++) delete[] argv[i]; delete[] argv; throw; } for (std::size_t i = 0; i < args.size() + 1; i++) delete[] argv[i]; delete[] argv; if (status != 0) { throw_(std::runtime_error, _("Failed to execute Python module")); } return NULL_VALUE; } option_t<python_interpreter_t> * python_interpreter_t::lookup_option(const char * p) { switch (*p) { case 'i': OPT(import_); break; } return NULL; } expr_t::ptr_op_t python_module_t::lookup(const symbol_t::kind_t kind, const string& name) { switch (kind) { case symbol_t::FUNCTION: DEBUG("python.interp", "Python lookup: " << name); if (module_globals.has_key(name.c_str())) { if (python::object obj = module_globals.get(name.c_str())) { if (PyModule_Check(obj.ptr())) { shared_ptr<python_module_t> mod; python_module_map_t::iterator i = python_session->modules_map.find(obj.ptr()); if (i == python_session->modules_map.end()) { mod.reset(new python_module_t(name, obj)); python_session->modules_map.insert (python_module_map_t::value_type(obj.ptr(), mod)); } else { mod = (*i).second; } return expr_t::op_t::wrap_value(scope_value(mod.get())); } else { return WRAP_FUNCTOR(python_interpreter_t::functor_t(obj, name)); } } } break; default: break; } return NULL; } expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind, const string& name) { // Give our superclass first dibs on symbol definitions if (expr_t::ptr_op_t op = session_t::lookup(kind, name)) return op; switch (kind) { case symbol_t::FUNCTION: if (is_initialized) return main_module->lookup(kind, name); break; case symbol_t::OPTION: { if (option_t<python_interpreter_t> * handler = lookup_option(name.c_str())) return MAKE_OPT_HANDLER(python_interpreter_t, handler); if (is_initialized) return main_module->lookup(symbol_t::FUNCTION, string("option_") + name); break; } case symbol_t::PRECOMMAND: { const char * p = name.c_str(); switch (*p) { case 'p': if (is_eq(p, "python")) return MAKE_FUNCTOR(python_interpreter_t::python_command); break; } } default: break; } return NULL; } namespace { object convert_value_to_python(const value_t& val) { switch (val.type()) { case value_t::VOID: // a null value (i.e., uninitialized) return object(); case value_t::BOOLEAN: // a boolean return object(val.to_boolean()); case value_t::DATETIME: // a date and time (Boost posix_time) return object(val.to_datetime()); case value_t::DATE: // a date (Boost gregorian::date) return object(val.to_date()); case value_t::INTEGER: // a signed integer value return object(val.to_long()); case value_t::AMOUNT: // a ledger::amount_t return object(val.as_amount()); case value_t::BALANCE: // a ledger::balance_t return object(val.as_balance()); case value_t::STRING: // a string object return object(handle<>(borrowed(str_to_py_unicode(val.as_string())))); case value_t::MASK: // a regular expression mask return object(val); case value_t::SEQUENCE: { // a vector of value_t objects list arglist; foreach (const value_t& elem, val.as_sequence()) arglist.append(elem); return arglist; } case value_t::SCOPE: // a pointer to a scope if (const scope_t * scope = val.as_scope()) { if (const post_t * post = dynamic_cast<const post_t *>(scope)) return object(ptr(post)); else if (const xact_t * xact = dynamic_cast<const xact_t *>(scope)) return object(ptr(xact)); else if (const account_t * account = dynamic_cast<const account_t *>(scope)) return object(ptr(account)); else if (const period_xact_t * period_xact = dynamic_cast<const period_xact_t *>(scope)) return object(ptr(period_xact)); else if (const auto_xact_t * auto_xact = dynamic_cast<const auto_xact_t *>(scope)) return object(ptr(auto_xact)); else throw_(std::logic_error, _("Cannot downcast scoped object to specific type")); } return object(); case value_t::ANY: // a pointer to an arbitrary object return object(val); } #if !defined(__clang__) return object(); #endif } } value_t python_interpreter_t::functor_t::operator()(call_scope_t& args) { try { std::signal(SIGINT, SIG_DFL); if (! PyCallable_Check(func.ptr())) { extract<value_t> val(func); DEBUG("python.interp", "Value of Python '" << name << "': " << val); std::signal(SIGINT, sigint_handler); if (val.check()) return val(); return NULL_VALUE; } else if (args.size() > 0) { list arglist; // jww (2009-11-05): What about a single argument which is a sequence, // rather than a sequence of arguments? if (args.value().is_sequence()) foreach (const value_t& value, args.value().as_sequence()) arglist.append(convert_value_to_python(value)); else arglist.append(convert_value_to_python(args.value())); if (PyObject * val = PyObject_CallObject(func.ptr(), python::tuple(arglist).ptr())) { extract<value_t> xval(val); value_t result; if (xval.check()) { result = xval(); DEBUG("python.interp", "Return from Python '" << name << "': " << result); Py_DECREF(val); } else { Py_DECREF(val); return NULL_VALUE; } std::signal(SIGINT, sigint_handler); return result; } else if (PyErr_Occurred()) { PyErr_Print(); throw_(calc_error, _f("Failed call to Python function '%1%'") % name); } else { assert(false); } } else { std::signal(SIGINT, sigint_handler); return call<value_t>(func.ptr()); } } catch (const error_already_set&) { std::signal(SIGINT, sigint_handler); PyErr_Print(); throw_(calc_error, _f("Failed call to Python function '%1%'") % name); } catch (...) { std::signal(SIGINT, sigint_handler); } std::signal(SIGINT, sigint_handler); return NULL_VALUE; } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/pyinterp.h�������������������������������������������������������������������������0000664�0000000�0000000�00000010642�14411236400�0015571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef INCLUDED_PYINTERP_H #define INCLUDED_PYINTERP_H #include "session.h" #if HAVE_BOOST_PYTHON namespace ledger { class python_module_t : public scope_t, public noncopyable { public: string module_name; python::object module_object; python::dict module_globals; explicit python_module_t(const string& name); explicit python_module_t(const string& name, python::object obj); void import_module(const string& name, bool import_direct = false); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); void define_global(const string& name, python::object obj) { module_globals[name] = obj; } virtual string description() { return module_name; } }; typedef std::map<PyObject *, shared_ptr<python_module_t> > python_module_map_t; class python_interpreter_t : public session_t { public: bool is_initialized; shared_ptr<python_module_t> main_module; python_module_map_t modules_map; shared_ptr<python_module_t> import_module(const string& name) { shared_ptr<python_module_t> mod(new python_module_t(name)); if (name != "__main__") main_module->define_global(name, mod->module_object); return mod; } python_interpreter_t() : session_t(), is_initialized(false) { TRACE_CTOR(python_interpreter_t, ""); } virtual ~python_interpreter_t() { TRACE_DTOR(python_interpreter_t); if (is_initialized) Py_Finalize(); } void initialize(); void hack_system_paths(); python::object import_option(const string& name); enum py_eval_mode_t { PY_EVAL_EXPR, PY_EVAL_STMT, PY_EVAL_MULTI }; python::object eval(std::istream& in, py_eval_mode_t mode = PY_EVAL_EXPR); python::object eval(const string& str, py_eval_mode_t mode = PY_EVAL_EXPR); python::object eval(const char * c_str, py_eval_mode_t mode = PY_EVAL_EXPR) { return eval(string(c_str), mode); } value_t python_command(call_scope_t& scope); class functor_t { functor_t(); protected: python::object func; public: string name; functor_t(python::object _func, const string& _name) : func(_func), name(_name) { TRACE_CTOR(functor_t, "python::object, const string&"); } functor_t(const functor_t& other) : func(other.func), name(other.name) { TRACE_CTOR(functor_t, "copy"); } virtual ~functor_t() throw() { TRACE_DTOR(functor_t); } virtual value_t operator()(call_scope_t& args); }; option_t<python_interpreter_t> * lookup_option(const char * p); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); OPTION_(python_interpreter_t, import_, DO_(str) { parent->import_option(str); }); }; extern shared_ptr<python_interpreter_t> python_session; } // namespace ledger #endif // HAVE_BOOST_PYTHON #endif // INCLUDED_PYINTERP_H ����������������������������������������������������������������������������������������������ledger-3.3.2/src/pyledger.cc������������������������������������������������������������������������0000664�0000000�0000000�00000003614�14411236400�0015671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "pyinterp.h" using namespace boost::python; namespace ledger { extern void initialize_for_python(); } BOOST_PYTHON_MODULE(ledger) { using namespace ledger; if (! python_session.get()) python_session.reset(new python_interpreter_t); set_session_context(python_session.get()); initialize_for_python(); } ��������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/pyutils.h��������������������������������������������������������������������������0000664�0000000�0000000�00000015637�14411236400�0015441�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef INCLUDED_PYUTILS_H #define INCLUDED_PYUTILS_H template <typename T, typename TfromPy> struct object_from_python { object_from_python() { boost::python::converter::registry::insert (&TfromPy::convertible, &TfromPy::construct, boost::python::type_id<T>()); } }; template <typename T, typename TtoPy, typename TfromPy> struct register_python_conversion { register_python_conversion() { boost::python::to_python_converter<T, TtoPy>(); object_from_python<T, TfromPy>(); } }; template <typename T> struct register_optional_to_python : public boost::noncopyable { struct optional_to_python { static PyObject * convert(const boost::optional<T>& value) { return boost::python::incref (value ? boost::python::to_python_value<T>()(*value) : boost::python::detail::none()); } }; struct optional_from_python { static void * convertible(PyObject * source) { using namespace boost::python::converter; if (source == Py_None) return source; const registration& converters(registered<T>::converters); if (implicit_rvalue_convertible_from_python(source, converters)) { rvalue_from_python_stage1_data data = rvalue_from_python_stage1(source, converters); return rvalue_from_python_stage2(source, data, converters); } return NULL; } static void construct(PyObject * source, boost::python::converter::rvalue_from_python_stage1_data * data) { using namespace boost::python::converter; const T value = typename boost::python::extract<T>(source); void * storage = ((rvalue_from_python_storage<boost::optional<T>>*) data)->storage.bytes; if (source == Py_None) // == None new (storage) boost::optional<T>(); // A Boost uninitialized value else new (storage) boost::optional<T>(value); data->convertible = storage; } }; explicit register_optional_to_python() { register_python_conversion<boost::optional<T>, optional_to_python, optional_from_python>(); } }; template <typename T1, typename T2> struct PairToTupleConverter { static PyObject * convert(const std::pair<T1, T2>& pair) { return boost::python::incref (boost::python::make_tuple(pair.first, pair.second).ptr()); } }; template <typename MapType> struct map_value_type_converter { map_value_type_converter() { boost::python::to_python_converter <typename MapType::value_type, PairToTupleConverter<const typename MapType::key_type, typename MapType::mapped_type> >(); } }; template <typename T> PyObject * str_to_py_unicode(const T& str) { using namespace boost::python; PyObject * uni = PyUnicode_FromString(str.c_str()); return object(handle<>(borrowed(uni))).ptr(); } namespace boost { namespace python { // Use expr to create the PyObject corresponding to x # define BOOST_PYTHON_RETURN_TO_PYTHON_BY_VALUE(T, expr, pytype)\ template <> struct to_python_value<T&> \ : detail::builtin_to_python \ { \ inline PyObject* operator()(T const& x) const \ { \ return (expr); \ } \ inline PyTypeObject const* get_pytype() const \ { \ return (pytype); \ } \ }; \ template <> struct to_python_value<T const&> \ : detail::builtin_to_python \ { \ inline PyObject* operator()(T const& x) const \ { \ return (expr); \ } \ inline PyTypeObject const* get_pytype() const \ { \ return (pytype); \ } \ }; # define BOOST_PYTHON_ARG_TO_PYTHON_BY_VALUE(T, expr) \ namespace converter \ { \ template <> struct arg_to_python< T > \ : handle<> \ { \ arg_to_python(T const& x) \ : python::handle<>(expr) {} \ }; \ } // Specialize argument and return value converters for T using expr # define BOOST_PYTHON_TO_PYTHON_BY_VALUE(T, expr, pytype) \ BOOST_PYTHON_RETURN_TO_PYTHON_BY_VALUE(T,expr, pytype) \ BOOST_PYTHON_ARG_TO_PYTHON_BY_VALUE(T,expr) } } // namespace boost::python //boost::python::register_ptr_to_python< boost::shared_ptr<Base> >(); #endif // INCLUDED_PYUTILS_H �������������������������������������������������������������������������������������������������ledger-3.3.2/src/query.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000035446�14411236400�0015233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "query.h" #include "op.h" namespace ledger { query_t::lexer_t::token_t query_t::lexer_t::next_token(query_t::lexer_t::token_t::kind_t tok_context) { if (token_cache.kind != token_t::UNKNOWN) { token_t tok = token_cache; token_cache = token_t(); return tok; } if (arg_i == arg_end) { if (begin == end || ++begin == end) { return token_t(token_t::END_REACHED); } else { arg_i = (*begin).as_string().begin(); arg_end = (*begin).as_string().end(); } } resume: switch (*arg_i) { case '\0': assert(false); break; case '\'': case '"': case '/': { string pat; char closing = *arg_i; bool found_closing = false; for (++arg_i; arg_i != arg_end; ++arg_i) { if (*arg_i == '\\') { if (++arg_i == arg_end) throw_(parse_error, _("Unexpected '\\' at end of pattern")); } else if (*arg_i == closing) { ++arg_i; found_closing = true; break; } pat.push_back(*arg_i); } if (! found_closing) throw_(parse_error, _f("Expected '%1%' at end of pattern") % closing); if (pat.empty()) throw_(parse_error, _("Match pattern is empty")); return token_t(token_t::TERM, pat); } } if (multiple_args && consume_next_arg) { consume_next_arg = false; token_t tok(token_t::TERM, string(arg_i, arg_end)); prev_arg_i = arg_i; arg_i = arg_end; return tok; } bool consume_next = false; switch (*arg_i) { case '\0': assert(false); break; case ' ': case '\t': case '\r': case '\n': if (++arg_i == arg_end) return next_token(tok_context); goto resume; case '(': ++arg_i; if (tok_context == token_t::TOK_EXPR) consume_whitespace = true; return token_t(token_t::LPAREN); case ')': ++arg_i; if (tok_context == token_t::TOK_EXPR) consume_whitespace = false; return token_t(token_t::RPAREN); case '&': ++arg_i; return token_t(token_t::TOK_AND); case '|': ++arg_i; return token_t(token_t::TOK_OR); case '!': ++arg_i; return token_t(token_t::TOK_NOT); case '@': ++arg_i; return token_t(token_t::TOK_PAYEE); case '#': ++arg_i; return token_t(token_t::TOK_CODE); case '%': ++arg_i; return token_t(token_t::TOK_META); case '=': ++arg_i; consume_next = true; return token_t(token_t::TOK_EQ); case '\\': consume_next = true; ++arg_i; // fall through... default: { string ident; for (; arg_i != arg_end; ++arg_i) { switch (*arg_i) { case '\0': assert(false); break; case ' ': case '\t': case '\n': case '\r': if (! multiple_args && ! consume_whitespace && ! consume_next_arg) goto test_ident; else ident.push_back(*arg_i); break; case ')': if (! consume_next && tok_context == token_t::TOK_EXPR) goto test_ident; // fall through... case '(': case '&': case '|': case '!': case '@': case '#': case '%': case '=': if (! consume_next && tok_context != token_t::TOK_EXPR) goto test_ident; // fall through... default: ident.push_back(*arg_i); break; } } consume_whitespace = false; test_ident: if (ident == "and") return token_t(token_t::TOK_AND); else if (ident == "or") return token_t(token_t::TOK_OR); else if (ident == "not") return token_t(token_t::TOK_NOT); else if (ident == "code") return token_t(token_t::TOK_CODE); else if (ident == "desc") return token_t(token_t::TOK_PAYEE); else if (ident == "payee") return token_t(token_t::TOK_PAYEE); else if (ident == "note") return token_t(token_t::TOK_NOTE); else if (ident == "tag") return token_t(token_t::TOK_META); else if (ident == "meta") return token_t(token_t::TOK_META); else if (ident == "data") return token_t(token_t::TOK_META); else if (ident == "show") return token_t(token_t::TOK_SHOW); else if (ident == "only") return token_t(token_t::TOK_ONLY); else if (ident == "bold") return token_t(token_t::TOK_BOLD); else if (ident == "for") return token_t(token_t::TOK_FOR); else if (ident == "since") return token_t(token_t::TOK_SINCE); else if (ident == "until") return token_t(token_t::TOK_UNTIL); else if (ident == "expr") { // The expr keyword takes the whole of the next string as its argument. consume_next_arg = true; return token_t(token_t::TOK_EXPR); } else return token_t(token_t::TERM, ident); } } return token_t(token_t::UNKNOWN); } void query_t::lexer_t::token_t::expected(char wanted) { throw_(parse_error, _f("Missing '%1%'") % wanted); } expr_t::ptr_op_t query_t::parser_t::parse_query_term(query_t::lexer_t::token_t::kind_t tok_context) { expr_t::ptr_op_t node; lexer_t::token_t tok = lexer.next_token(tok_context); switch (tok.kind) { case lexer_t::token_t::TOK_SHOW: case lexer_t::token_t::TOK_ONLY: case lexer_t::token_t::TOK_BOLD: case lexer_t::token_t::TOK_FOR: case lexer_t::token_t::TOK_SINCE: case lexer_t::token_t::TOK_UNTIL: case lexer_t::token_t::END_REACHED: lexer.push_token(tok); break; case lexer_t::token_t::TOK_CODE: case lexer_t::token_t::TOK_PAYEE: case lexer_t::token_t::TOK_NOTE: case lexer_t::token_t::TOK_ACCOUNT: case lexer_t::token_t::TOK_META: case lexer_t::token_t::TOK_EXPR: node = parse_query_term(tok.kind); if (! node) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol()); break; case lexer_t::token_t::TERM: assert(tok.value); switch (tok_context) { case lexer_t::token_t::TOK_EXPR: node = expr_t(*tok.value).get_op(); break; case lexer_t::token_t::TOK_META: { node = new expr_t::op_t(expr_t::op_t::O_CALL); expr_t::ptr_op_t ident = new expr_t::op_t(expr_t::op_t::IDENT); ident->set_ident("has_tag"); node->set_left(ident); expr_t::ptr_op_t arg1 = new expr_t::op_t(expr_t::op_t::VALUE); arg1->set_value(mask_t(*tok.value)); tok = lexer.peek_token(tok_context); if (tok.kind == lexer_t::token_t::TOK_EQ) { tok = lexer.next_token(tok_context); tok = lexer.next_token(tok_context); if (tok.kind != lexer_t::token_t::TERM) throw_(parse_error, _("Metadata equality operator not followed by term")); expr_t::ptr_op_t arg2 = new expr_t::op_t(expr_t::op_t::VALUE); assert(tok.value); arg2->set_value(mask_t(*tok.value)); node->set_right(expr_t::op_t::new_node (expr_t::op_t::O_SEQ, expr_t::op_t::new_node (expr_t::op_t::O_CONS, arg1, arg2))); } else { node->set_right(arg1); } break; } default: { node = new expr_t::op_t(expr_t::op_t::O_MATCH); expr_t::ptr_op_t ident = new expr_t::op_t(expr_t::op_t::IDENT); switch (tok_context) { case lexer_t::token_t::TOK_ACCOUNT: ident->set_ident("account"); break; case lexer_t::token_t::TOK_PAYEE: ident->set_ident("payee"); break; case lexer_t::token_t::TOK_CODE: ident->set_ident("code"); break; case lexer_t::token_t::TOK_NOTE: ident->set_ident("note"); break; default: assert(false); break; } expr_t::ptr_op_t mask = new expr_t::op_t(expr_t::op_t::VALUE); DEBUG("query.mask", "Mask from string: " << *tok.value); mask->set_value(mask_t(*tok.value)); DEBUG("query.mask", "Mask is: " << mask->as_value().as_mask().str()); node->set_left(ident); node->set_right(mask); } } break; case lexer_t::token_t::LPAREN: node = parse_query_expr(tok_context, true); tok = lexer.next_token(tok_context); if (tok.kind != lexer_t::token_t::RPAREN) tok.expected(')'); break; default: lexer.push_token(tok); break; } return node; } expr_t::ptr_op_t query_t::parser_t::parse_unary_expr(lexer_t::token_t::kind_t tok_context) { expr_t::ptr_op_t node; lexer_t::token_t tok = lexer.next_token(tok_context); switch (tok.kind) { case lexer_t::token_t::TOK_NOT: { expr_t::ptr_op_t term(parse_query_term(tok_context)); if (! term) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol()); node = new expr_t::op_t(expr_t::op_t::O_NOT); node->set_left(term); break; } default: lexer.push_token(tok); node = parse_query_term(tok_context); break; } return node; } expr_t::ptr_op_t query_t::parser_t::parse_and_expr(lexer_t::token_t::kind_t tok_context) { if (expr_t::ptr_op_t node = parse_unary_expr(tok_context)) { while (true) { lexer_t::token_t tok = lexer.next_token(tok_context); if (tok.kind == lexer_t::token_t::TOK_AND) { expr_t::ptr_op_t prev(node); node = new expr_t::op_t(expr_t::op_t::O_AND); node->set_left(prev); node->set_right(parse_unary_expr(tok_context)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol()); } else { lexer.push_token(tok); break; } } return node; } return expr_t::ptr_op_t(); } expr_t::ptr_op_t query_t::parser_t::parse_or_expr(lexer_t::token_t::kind_t tok_context) { if (expr_t::ptr_op_t node = parse_and_expr(tok_context)) { while (true) { lexer_t::token_t tok = lexer.next_token(tok_context); if (tok.kind == lexer_t::token_t::TOK_OR) { expr_t::ptr_op_t prev(node); node = new expr_t::op_t(expr_t::op_t::O_OR); node->set_left(prev); node->set_right(parse_and_expr(tok_context)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol()); } else { lexer.push_token(tok); break; } } return node; } return expr_t::ptr_op_t(); } expr_t::ptr_op_t query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context, bool subexpression) { expr_t::ptr_op_t limiter; while (expr_t::ptr_op_t next = parse_or_expr(tok_context)) { if (! limiter) { limiter = next; } else { expr_t::ptr_op_t prev(limiter); limiter = new expr_t::op_t(expr_t::op_t::O_OR); limiter->set_left(prev); limiter->set_right(next); } } if (! subexpression) { if (limiter) query_map.insert (query_map_t::value_type (QUERY_LIMIT, predicate_t(limiter, what_to_keep).print_to_str())); lexer_t::token_t tok = lexer.peek_token(tok_context); while (tok.kind != lexer_t::token_t::END_REACHED) { switch (tok.kind) { case lexer_t::token_t::TOK_SHOW: case lexer_t::token_t::TOK_ONLY: case lexer_t::token_t::TOK_BOLD: { lexer.next_token(tok_context); kind_t kind; switch (tok.kind) { case lexer_t::token_t::TOK_SHOW: kind = QUERY_SHOW; break; case lexer_t::token_t::TOK_ONLY: kind = QUERY_ONLY; break; case lexer_t::token_t::TOK_BOLD: kind = QUERY_BOLD; break; default: break; } expr_t::ptr_op_t node; while (expr_t::ptr_op_t next = parse_or_expr(tok_context)) { if (! node) { node = next; } else { expr_t::ptr_op_t prev(node); node = new expr_t::op_t(expr_t::op_t::O_OR); node->set_left(prev); node->set_right(next); } } if (node) query_map.insert (query_map_t::value_type (kind, predicate_t(node, what_to_keep).print_to_str())); break; } case lexer_t::token_t::TOK_FOR: case lexer_t::token_t::TOK_SINCE: case lexer_t::token_t::TOK_UNTIL: { tok = lexer.next_token(tok_context); string for_string; if (tok.kind == lexer_t::token_t::TOK_SINCE) for_string = "since"; else if (tok.kind == lexer_t::token_t::TOK_UNTIL) for_string = "until"; lexer.consume_next_arg = true; tok = lexer.peek_token(tok_context); while (tok.kind != lexer_t::token_t::END_REACHED) { tok = lexer.next_token(tok_context); assert(tok.kind == lexer_t::token_t::TERM); if (*tok.value == "show" || *tok.value == "bold" || *tok.value == "for" || *tok.value == "since" || *tok.value == "until") { lexer.token_cache = lexer_t::token_t(); lexer.arg_i = lexer.prev_arg_i; lexer.consume_next_arg = false; break; } if (! for_string.empty()) for_string += " "; for_string += *tok.value; lexer.consume_next_arg = true; tok = lexer.peek_token(tok_context); } if (! for_string.empty()) query_map.insert(query_map_t::value_type(QUERY_FOR, for_string)); break; } default: goto done; } tok = lexer.peek_token(tok_context); } done: ; } return limiter; } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/query.h����������������������������������������������������������������������������0000664�0000000�0000000�00000024056�14411236400�0015070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file predicate.h * @author John Wiegley * * @ingroup expr */ #ifndef INCLUDED_QUERY_H #define INCLUDED_QUERY_H #include "predicate.h" namespace ledger { class query_t { protected: class parser_t; public: class lexer_t { friend class query_t; friend class parser_t; value_t::sequence_t::const_iterator begin; value_t::sequence_t::const_iterator end; string::const_iterator prev_arg_i; string::const_iterator arg_i; string::const_iterator arg_end; bool consume_whitespace; bool consume_next_arg; bool multiple_args; public: struct token_t { enum kind_t { UNKNOWN, LPAREN, RPAREN, TOK_NOT, TOK_AND, TOK_OR, TOK_EQ, TOK_CODE, TOK_PAYEE, TOK_NOTE, TOK_ACCOUNT, TOK_META, TOK_EXPR, TOK_SHOW, TOK_ONLY, TOK_BOLD, TOK_FOR, TOK_SINCE, TOK_UNTIL, TERM, END_REACHED } kind; optional<string> value; explicit token_t(kind_t _kind = UNKNOWN, const optional<string>& _value = none) : kind(_kind), value(_value) { TRACE_CTOR(query_t::lexer_t::token_t, ""); } token_t(const token_t& tok) : kind(tok.kind), value(tok.value) { TRACE_CTOR(query_t::lexer_t::token_t, "copy"); } ~token_t() throw() { TRACE_DTOR(query_t::lexer_t::token_t); } token_t& operator=(const token_t& tok) { if (this != &tok) { kind = tok.kind; value = tok.value; } return *this; } operator bool() const { return kind != END_REACHED; } string to_string() const { switch (kind) { case UNKNOWN: return "UNKNOWN"; case LPAREN: return "LPAREN"; case RPAREN: return "RPAREN"; case TOK_NOT: return "TOK_NOT"; case TOK_AND: return "TOK_AND"; case TOK_OR: return "TOK_OR"; case TOK_EQ: return "TOK_EQ"; case TOK_CODE: return "TOK_CODE"; case TOK_PAYEE: return "TOK_PAYEE"; case TOK_NOTE: return "TOK_NOTE"; case TOK_ACCOUNT: return "TOK_ACCOUNT"; case TOK_META: return "TOK_META"; case TOK_EXPR: return "TOK_EXPR"; case TOK_SHOW: return "TOK_SHOW"; case TOK_ONLY: return "TOK_ONLY"; case TOK_BOLD: return "TOK_BOLD"; case TOK_FOR: return "TOK_FOR"; case TOK_SINCE: return "TOK_SINCE"; case TOK_UNTIL: return "TOK_UNTIL"; case TERM: return string("TERM(") + *value + ")"; case END_REACHED: return "END_REACHED"; } } string symbol() const { switch (kind) { case LPAREN: return "("; case RPAREN: return ")"; case TOK_NOT: return "not"; case TOK_AND: return "and"; case TOK_OR: return "or"; case TOK_EQ: return "="; case TOK_CODE: return "code"; case TOK_PAYEE: return "payee"; case TOK_NOTE: return "note"; case TOK_ACCOUNT: return "account"; case TOK_META: return "meta"; case TOK_EXPR: return "expr"; case TOK_SHOW: return "show"; case TOK_ONLY: return "only"; case TOK_BOLD: return "bold"; case TOK_FOR: return "for"; case TOK_SINCE: return "since"; case TOK_UNTIL: return "until"; case END_REACHED: return "<EOF>"; case TERM: assert(false); return "<TERM>"; case UNKNOWN: assert(false); return "<UNKNOWN>"; } #if !defined(__clang__) return "<ERROR>"; #endif } void expected(char wanted); }; token_t token_cache; lexer_t(value_t::sequence_t::const_iterator _begin, value_t::sequence_t::const_iterator _end, bool _multiple_args = true) : begin(_begin), end(_end), consume_whitespace(false), consume_next_arg(false), multiple_args(_multiple_args) { assert(begin != end); arg_i = (*begin).as_string().begin(); arg_end = (*begin).as_string().end(); TRACE_CTOR(query_t::lexer_t, ""); } lexer_t(const lexer_t& lexer) : begin(lexer.begin), end(lexer.end), arg_i(lexer.arg_i), arg_end(lexer.arg_end), consume_whitespace(lexer.consume_whitespace), consume_next_arg(lexer.consume_next_arg), multiple_args(lexer.multiple_args), token_cache(lexer.token_cache) { TRACE_CTOR(query_t::lexer_t, "copy"); } ~lexer_t() throw() { TRACE_DTOR(query_t::lexer_t); } token_t next_token(token_t::kind_t tok_context = token_t::UNKNOWN); void push_token(token_t tok) { assert(token_cache.kind == token_t::UNKNOWN); token_cache = tok; } token_t peek_token(token_t::kind_t tok_context = token_t::UNKNOWN) { if (token_cache.kind == token_t::UNKNOWN) token_cache = next_token(tok_context); return token_cache; } }; enum kind_t { QUERY_LIMIT, QUERY_SHOW, QUERY_ONLY, QUERY_BOLD, QUERY_FOR }; typedef std::map<kind_t, string> query_map_t; protected: class parser_t { friend class query_t; value_t args; lexer_t lexer; keep_details_t what_to_keep; query_map_t query_map; expr_t::ptr_op_t parse_query_term(lexer_t::token_t::kind_t tok_context); expr_t::ptr_op_t parse_unary_expr(lexer_t::token_t::kind_t tok_context); expr_t::ptr_op_t parse_and_expr(lexer_t::token_t::kind_t tok_context); expr_t::ptr_op_t parse_or_expr(lexer_t::token_t::kind_t tok_context); expr_t::ptr_op_t parse_query_expr(lexer_t::token_t::kind_t tok_context, bool subexpression = false); public: parser_t(const value_t& _args, const keep_details_t& _what_to_keep = keep_details_t(), bool multiple_args = true) : args(_args), lexer(args.begin(), args.end(), multiple_args), what_to_keep(_what_to_keep) { TRACE_CTOR(query_t::parser_t, "value_t, keep_details_t, bool"); } parser_t(const parser_t& other) : args(other.args), lexer(other.lexer) { TRACE_CTOR(query_t::parser_t, "copy"); } ~parser_t() throw() { TRACE_DTOR(query_t::parser_t); } expr_t::ptr_op_t parse(bool subexpression = false) { return parse_query_expr(lexer_t::token_t::TOK_ACCOUNT, subexpression); } bool tokens_remaining() { lexer_t::token_t tok = lexer.peek_token(); assert(tok.kind != lexer_t::token_t::UNKNOWN); return tok.kind != lexer_t::token_t::END_REACHED; } }; optional<parser_t> parser; query_map_t predicates; public: query_t() { TRACE_CTOR(query_t, ""); } query_t(const query_t& other) : parser(other.parser), predicates(other.predicates) { TRACE_CTOR(query_t, "copy"); } query_t(const string& arg, const keep_details_t& what_to_keep = keep_details_t(), bool multiple_args = true) { if (! arg.empty()) { value_t temp(string_value(arg)); parse_args(temp.to_sequence(), what_to_keep, multiple_args); } TRACE_CTOR(query_t, "string, keep_details_t, bool"); } query_t(const value_t& args, const keep_details_t& what_to_keep = keep_details_t(), bool multiple_args = true) { if (! args.empty()) parse_args(args, what_to_keep, multiple_args); TRACE_CTOR(query_t, "value_t, keep_details_t, bool"); } virtual ~query_t() { TRACE_DTOR(query_t); } expr_t::ptr_op_t parse_args(const value_t& args, const keep_details_t& what_to_keep = keep_details_t(), bool multiple_args = true, bool subexpression = false) { if (! parser) parser = parser_t(args, what_to_keep, multiple_args); return parser->parse(subexpression); } bool has_query(const kind_t& id) const { return parser && parser->query_map.find(id) != parser->query_map.end(); } string get_query(const kind_t& id) const { if (parser) { query_map_t::const_iterator i = parser->query_map.find(id); if (i != parser->query_map.end()) return (*i).second; } return empty_string; } bool tokens_remaining() { return parser && parser->tokens_remaining(); } }; } // namespace ledger #endif // INCLUDED_QUERY_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/quotes.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000007372�14411236400�0015403�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "amount.h" #include "commodity.h" #include "pool.h" #include "quotes.h" namespace ledger { optional<price_point_t> commodity_quote_from_script(commodity_t& commodity, const commodity_t * exchange_commodity) { DEBUG("commodity.download", "downloading quote for symbol " << commodity.symbol()); #if DEBUG_ON if (exchange_commodity) DEBUG("commodity.download", " in terms of commodity " << exchange_commodity->symbol()); #endif char buf[256]; buf[0] = '\0'; string getquote_cmd("getquote \""); getquote_cmd += commodity.symbol(); getquote_cmd += "\" \""; if (exchange_commodity) getquote_cmd += exchange_commodity->symbol(); getquote_cmd += "\""; DEBUG("commodity.download", "invoking command: " << getquote_cmd); bool success = true; #if !defined(_WIN32) && !defined(__CYGWIN__) if (FILE * fp = popen(getquote_cmd.c_str(), "r")) { if (std::feof(fp) || ! std::fgets(buf, 255, fp)) success = false; if (pclose(fp) != 0) success = false; } else { success = false; } if (success && buf[0]) { if (char * p = std::strchr(buf, '\n')) *p = '\0'; DEBUG("commodity.download", "downloaded quote: " << buf); if (optional<std::pair<commodity_t *, price_point_t> > point = commodity_pool_t::current_pool->parse_price_directive(buf)) { if (commodity_pool_t::current_pool->price_db) { ofstream database(*commodity_pool_t::current_pool->price_db, std::ios_base::out | std::ios_base::app); database << "P " << format_datetime(point->second.when, FMT_WRITTEN) << " " << commodity.symbol() << " " << point->second.price << std::endl; } return point->second; } } else { DEBUG("commodity.download", "Failed to download price for '" << commodity.symbol() << "' (command: \"getquote " << commodity.symbol() << " " << (exchange_commodity ? exchange_commodity->symbol() : "''") << "\")"); // Don't try to download this commodity again. commodity.add_flags(COMMODITY_NOMARKET); } #endif return none; } } // namespace ledger ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/quotes.h���������������������������������������������������������������������������0000664�0000000�0000000�00000003640�14411236400�0015237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup extra */ /** * @file quotes.h * @author John Wiegley * * @ingroup extra */ #ifndef INCLUDED_QUOTES_H #define INCLUDED_QUOTES_H namespace ledger { optional<price_point_t> commodity_quote_from_script(commodity_t& commodity, const commodity_t * exchange_commodity); } // namespace ledger #endif // INCLUDED_QUOTES_H ������������������������������������������������������������������������������������������������ledger-3.3.2/src/report.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000145127�14411236400�0015377�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "report.h" #include "session.h" #include "pool.h" #include "format.h" #include "query.h" #include "output.h" #include "print.h" #include "iterators.h" #include "filters.h" #include "precmd.h" #include "select.h" #include "stats.h" #include "generate.h" #include "draft.h" #include "convert.h" #include "ptree.h" #include "emacs.h" namespace ledger { void report_t::normalize_options(const string& verb) { // Patch up some of the reporting options based on what kind of // command it was. #ifdef HAVE_ISATTY if (! HANDLED(force_color)) { if (! HANDLED(no_color) && isatty(STDOUT_FILENO)) HANDLER(color).on("?normalize"); if (HANDLED(color) && ! isatty(STDOUT_FILENO)) HANDLER(color).off(); } else { HANDLER(color).on("?normalize"); } if (! HANDLED(force_pager)) { if (HANDLED(pager_) && ! isatty(STDOUT_FILENO)) HANDLER(pager_).off(); } #endif if (HANDLED(output_)) { if (HANDLED(color) && ! HANDLED(force_color)) HANDLER(color).off(); if (HANDLED(pager_) && ! HANDLED(force_pager)) HANDLER(pager_).off(); } item_t::use_aux_date = (HANDLED(aux_date) && ! HANDLED(primary_date)); commodity_pool_t::current_pool->keep_base = HANDLED(base); commodity_pool_t::current_pool->get_quotes = session.HANDLED(download); if (session.HANDLED(price_exp_)) commodity_pool_t::current_pool->quote_leeway = lexical_cast<long>(session.HANDLER(price_exp_).value) * 3600L; if (session.HANDLED(price_db_)) commodity_pool_t::current_pool->price_db = session.HANDLER(price_db_).str(); else commodity_pool_t::current_pool->price_db = none; if (HANDLED(date_format_)) set_date_format(HANDLER(date_format_).str().c_str()); if (HANDLED(datetime_format_)) set_datetime_format(HANDLER(datetime_format_).str().c_str()); if (HANDLED(start_of_week_)) { if (optional<date_time::weekdays> weekday = string_to_day_of_week(HANDLER(start_of_week_).str())) start_of_week = *weekday; } long meta_width = -1; if (! HANDLED(prepend_format_) && HANDLED(meta_)) { if (! HANDLED(meta_width_)) { string::size_type i = HANDLER(meta_).str().find(':'); if (i != string::npos) { HANDLED(meta_width_).on("?normalize", string(HANDLER(meta_).str(), i + 1)); HANDLED(meta_).on("?normalize", string(HANDLER(meta_).str(), 0, i)); } } if (HANDLED(meta_width_)) { HANDLER(prepend_format_) .on("?normalize", string("%(justify(truncated(tag(\"") + HANDLER(meta_).str() + "\"), " + HANDLED(meta_width_).value + " - 1), " + HANDLED(meta_width_).value + "))"); meta_width = lexical_cast<long>(HANDLED(meta_width_).value); } else { HANDLER(prepend_format_) .on("?normalize", string("%(tag(\"") + HANDLER(meta_).str() + "\"))"); } } if (verb == "print" || verb == "xact" || verb == "dump") { HANDLER(related_all).parent = this; HANDLER(related_all).on("?normalize"); } else if (verb == "equity") { HANDLER(equity).on("?normalize"); } if (verb[0] != 'b' && verb[0] != 'r') HANDLER(base).on("?normalize"); // If a time period was specified with -p, check whether it also gave a // begin and/or end to the report period (though these can be overridden // using -b or -e). Then, if no _duration_ was specified (such as monthly), // then ignore the period since the begin/end are the only interesting // details. if (HANDLED(period_)) normalize_period(); // If -j or -J were specified, set the appropriate format string now so as // to avoid option ordering issues were we to have done it during the // initial parsing of the options. if (HANDLED(amount_data)) { HANDLER(format_).on("?normalize", HANDLER(plot_amount_format_).value); } else if (HANDLED(total_data)) { HANDLER(format_).on("?normalize", HANDLER(plot_total_format_).value); } // If the --exchange (-X) option was used, parse out any final price // settings that may be there. if (HANDLED(exchange_) && HANDLER(exchange_).str().find('=') != string::npos) { value_t(0L).exchange_commodities(HANDLER(exchange_).str(), true, terminus); } if (HANDLED(percent)) { commodity_t::decimal_comma_by_default = false; if (HANDLED(market)) { HANDLER(total_) .on("?normalize", "(__tmp = market(parent.total, value_date, exchange);" " ((is_account & parent & __tmp) ?" " percent(scrub(market(total, value_date, exchange)), " " scrub(__tmp)) : 0))"); } } if (HANDLED(immediate) && HANDLED(market)) { HANDLER(amount_) .on("?normalize", "market(amount_expr, value_date, exchange)"); } long cols = 0; #ifdef HAVE_IOCTL struct winsize ws; #endif if (HANDLED(columns_)) cols = lexical_cast<long>(HANDLER(columns_).value); else if (const char * columns = std::getenv("COLUMNS")) cols = lexical_cast<long>(columns); #ifdef HAVE_IOCTL else if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1) cols = ws.ws_col; #endif else cols = 80L; if (meta_width > 0) cols -= meta_width; if (cols > 0) { DEBUG("auto.columns", "cols = " << cols); long date_width = (HANDLED(date_width_) ? lexical_cast<long>(HANDLER(date_width_).str()) : static_cast<long> (format_date(CURRENT_DATE(),FMT_PRINTED).length())); long payee_width = (HANDLED(payee_width_) ? lexical_cast<long>(HANDLER(payee_width_).str()) : long(double(cols) * 0.263157)); long account_width = (HANDLED(account_width_) ? lexical_cast<long>(HANDLER(account_width_).str()) : long(double(cols) * 0.302631)); long amount_width = (HANDLED(amount_width_) ? lexical_cast<long>(HANDLER(amount_width_).str()) : long(double(cols) * 0.157894)); long total_width = (HANDLED(total_width_) ? lexical_cast<long>(HANDLER(total_width_).str()) : amount_width); DEBUG("auto.columns", "date_width = " << date_width); DEBUG("auto.columns", "payee_width = " << payee_width); DEBUG("auto.columns", "account_width = " << account_width); DEBUG("auto.columns", "amount_width = " << amount_width); DEBUG("auto.columns", "total_width = " << total_width); if (! HANDLED(date_width_) && ! HANDLED(payee_width_) && ! HANDLED(account_width_) && ! HANDLED(amount_width_) && ! HANDLED(total_width_)) { long total = (4 /* the spaces between */ + date_width + payee_width + account_width + amount_width + total_width + (HANDLED(dc) ? 1 + amount_width : 0)); while (total > cols && account_width > 5 && payee_width > 5) { DEBUG("auto.columns", "adjusting account down"); if (total > cols) { --account_width; --total; if (total > cols) { --account_width; --total; } } if (total > cols) { --payee_width; --total; } DEBUG("auto.columns", "account_width now = " << account_width); } } if (! HANDLED(meta_width_)) HANDLER(meta_width_).value = "0"; if (! HANDLED(prepend_width_)) HANDLER(prepend_width_).value = "0"; if (! HANDLED(date_width_)) HANDLER(date_width_).value = to_string(date_width); if (! HANDLED(payee_width_)) HANDLER(payee_width_).value = to_string(payee_width); if (! HANDLED(account_width_)) HANDLER(account_width_).value = to_string(account_width); if (! HANDLED(amount_width_)) HANDLER(amount_width_).value = to_string(amount_width); if (! HANDLED(total_width_)) HANDLER(total_width_).value = to_string(total_width); } } void report_t::normalize_period() { date_interval_t interval(HANDLER(period_).str()); optional<date_t> begin = interval.begin(); optional<date_t> end = interval.end(); if (! HANDLED(begin_) && begin) { string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; HANDLER(limit_).on(string("?normalize"), predicate); } if (! HANDLED(end_) && end) { string predicate = "date<[" + to_iso_extended_string(*end) + "]"; HANDLER(limit_).on(string("?normalize"), predicate); } if (! interval.duration) HANDLER(period_).off(); else if (! HANDLED(sort_all_)) HANDLER(sort_xacts_).on("?normalize"); } void report_t::parse_query_args(const value_t& args, const string& whence) { query_t query(args, what_to_keep()); if (query.has_query(query_t::QUERY_LIMIT)) { HANDLER(limit_).on(whence, query.get_query(query_t::QUERY_LIMIT)); DEBUG("report.predicate", "Limit predicate = " << HANDLER(limit_).str()); } if (query.has_query(query_t::QUERY_ONLY)) { HANDLER(only_).on(whence, query.get_query(query_t::QUERY_ONLY)); DEBUG("report.predicate", "Only predicate = " << HANDLER(only_).str()); } if (query.has_query(query_t::QUERY_SHOW)) { HANDLER(display_).on(whence, query.get_query(query_t::QUERY_SHOW)); DEBUG("report.predicate", "Display predicate = " << HANDLER(display_).str()); } if (query.has_query(query_t::QUERY_BOLD)) { HANDLER(bold_if_).on(whence, query.get_query(query_t::QUERY_BOLD)); DEBUG("report.predicate", "Bolding predicate = " << HANDLER(bold_if_).str()); } if (query.has_query(query_t::QUERY_FOR)) { HANDLER(period_).on(whence, query.get_query(query_t::QUERY_FOR)); DEBUG("report.predicate", "Report period = " << HANDLER(period_).str()); normalize_period(); // it needs normalization } } namespace { struct posts_flusher { post_handler_ptr handler; report_t& report; posts_flusher(post_handler_ptr _handler, report_t& _report) : handler(_handler), report(_report) { TRACE_CTOR(posts_flusher, "post_handler_ptr, report_t&"); } ~posts_flusher() throw() { TRACE_DTOR(posts_flusher); } void operator()(const value_t&) { report.session.journal->clear_xdata(); } }; } void report_t::posts_report(post_handler_ptr handler) { handler = chain_post_handlers(handler, *this); if (HANDLED(group_by_)) { unique_ptr<post_splitter> splitter(new post_splitter(handler, *this, HANDLER(group_by_).expr)); splitter->set_postflush_func(posts_flusher(handler, *this)); handler = post_handler_ptr(splitter.release()); } handler = chain_pre_post_handlers(handler, *this); journal_posts_iterator walker(*session.journal.get()); pass_down_posts<journal_posts_iterator>(handler, walker); if (! HANDLED(group_by_)) posts_flusher(handler, *this)(value_t()); } void report_t::generate_report(post_handler_ptr handler) { handler = chain_handlers(handler, *this); generate_posts_iterator walker (session, HANDLED(seed_) ? lexical_cast<unsigned int>(HANDLER(seed_).str()) : 0, HANDLED(head_) ? lexical_cast<unsigned int>(HANDLER(head_).str()) : 50); pass_down_posts<generate_posts_iterator>(handler, walker); } void report_t::xact_report(post_handler_ptr handler, xact_t& xact) { handler = chain_handlers(handler, *this); xact_posts_iterator walker(xact); pass_down_posts<xact_posts_iterator>(handler, walker); xact.clear_xdata(); } namespace { struct accounts_title_printer { acct_handler_ptr handler; report_t& report; accounts_title_printer(acct_handler_ptr _handler, report_t& _report) : handler(_handler), report(_report) {} void operator()(const value_t& val) { if (! report.HANDLED(no_titles)) { std::ostringstream buf; val.print(buf); handler->title(buf.str()); } } }; struct accounts_flusher { acct_handler_ptr handler; report_t& report; accounts_flusher(acct_handler_ptr _handler, report_t& _report) : handler(_handler), report(_report) {} void operator()(const value_t&) { report.HANDLER(amount_).expr.mark_uncompiled(); report.HANDLER(total_).expr.mark_uncompiled(); report.HANDLER(display_amount_).expr.mark_uncompiled(); report.HANDLER(display_total_).expr.mark_uncompiled(); report.HANDLER(revalued_total_).expr.mark_uncompiled(); if (report.HANDLED(display_)) { DEBUG("report.predicate", "Display predicate = " << report.HANDLER(display_).str()); if (! report.HANDLED(sort_)) { basic_accounts_iterator iter(*report.session.journal->master); pass_down_accounts<basic_accounts_iterator> (handler, iter, predicate_t(report.HANDLER(display_).str(), report.what_to_keep()), report); } else { expr_t sort_expr(report.HANDLER(sort_).str()); sort_expr.set_context(&report); sorted_accounts_iterator iter( *report.session.journal->master, sort_expr, report, report.HANDLED(flat)); pass_down_accounts<sorted_accounts_iterator> (handler, iter, predicate_t(report.HANDLER(display_).str(), report.what_to_keep()), report); } } else { if (! report.HANDLED(sort_)) { basic_accounts_iterator iter(*report.session.journal->master); pass_down_accounts<basic_accounts_iterator>(handler, iter); } else { expr_t sort_expr(report.HANDLER(sort_).str()); sort_expr.set_context(&report); sorted_accounts_iterator iter( *report.session.journal->master, sort_expr, report, report.HANDLED(flat)); pass_down_accounts<sorted_accounts_iterator>(handler, iter); } } report.session.journal->clear_xdata(); } }; } void report_t::accounts_report(acct_handler_ptr handler) { post_handler_ptr chain = chain_post_handlers(post_handler_ptr(new ignore_posts), *this, /* for_accounts_report= */ true); if (HANDLED(group_by_)) { unique_ptr<post_splitter> splitter(new post_splitter(chain, *this, HANDLER(group_by_).expr)); splitter->set_preflush_func(accounts_title_printer(handler, *this)); splitter->set_postflush_func(accounts_flusher(handler, *this)); chain = post_handler_ptr(splitter.release()); } chain = chain_pre_post_handlers(chain, *this); // The lifetime of the chain object controls the lifetime of all temporary // objects created within it during the call to pass_down_posts, which will // be needed later by the pass_down_accounts. journal_posts_iterator walker(*session.journal.get()); pass_down_posts<journal_posts_iterator>(chain, walker); if (! HANDLED(group_by_)) accounts_flusher(handler, *this)(value_t()); } void report_t::commodities_report(post_handler_ptr handler) { handler = chain_handlers(handler, *this); posts_commodities_iterator * walker(new posts_commodities_iterator(*session.journal.get())); try { pass_down_posts<posts_commodities_iterator>(handler, *walker); } catch (...) { #if VERIFY_ON IF_VERIFY() { // If --verify was used, clean up the posts_commodities_iterator. // Otherwise, just leak like a sieve. checked_delete(walker); } #endif throw; } session.journal->clear_xdata(); } value_t report_t::display_value(const value_t& val) { value_t temp(val.strip_annotations(what_to_keep())); if (HANDLED(base)) return temp; else return temp.unreduced(); } namespace { value_t top_amount(const value_t& val) { switch (val.type()) { case value_t::BALANCE: return (*val.as_balance().amounts.begin()).second; case value_t::SEQUENCE: { return top_amount(*val.as_sequence().begin()); } default: return val; } } } value_t report_t::fn_top_amount(call_scope_t& args) { return top_amount(args[0]); } value_t report_t::fn_amount_expr(call_scope_t& scope) { return HANDLER(amount_).expr.calc(scope); } value_t report_t::fn_total_expr(call_scope_t& scope) { return HANDLER(total_).expr.calc(scope); } value_t report_t::fn_display_amount(call_scope_t& scope) { return HANDLER(display_amount_).expr.calc(scope); } value_t report_t::fn_display_total(call_scope_t& scope) { return HANDLER(display_total_).expr.calc(scope); } value_t report_t::fn_should_bold(call_scope_t& scope) { if (HANDLED(bold_if_)) return HANDLER(bold_if_).expr.calc(scope); else return false; } value_t report_t::fn_averaged_lots(call_scope_t& args) { if (args.has<balance_t>(0)) return average_lot_prices(args.get<balance_t>(0)); else return args[0]; } value_t report_t::fn_market(call_scope_t& args) { value_t result; value_t arg0 = args[0]; datetime_t moment; if (args.has<datetime_t>(1)) moment = args.get<datetime_t>(1); if (arg0.is_string()) { amount_t tmp(1L); commodity_t * commodity = commodity_pool_t::current_pool->find_or_create(arg0.as_string()); tmp.set_commodity(*commodity); arg0 = tmp; } string target_commodity; if (args.has<string>(2)) target_commodity = args.get<string>(2); if (! target_commodity.empty()) result = arg0.exchange_commodities(target_commodity, /* add_prices= */ false, moment); else result = arg0.value(moment); return ! result.is_null() ? result : arg0; } value_t report_t::fn_get_at(call_scope_t& args) { std::size_t index = static_cast<std::size_t>(args.get<long>(1)); if (index == 0) { if (! args[0].is_sequence()) return args[0]; } else if (! args[0].is_sequence()) { throw_(std::runtime_error, _f("Attempting to get argument at index %1% from %2%") % index % args[0].label()); } value_t::sequence_t& seq(args[0].as_sequence_lval()); if (index >= seq.size()) throw_(std::runtime_error, _f("Attempting to get index %1% from %2% with %3% elements") % index % args[0].label() % seq.size()); return seq[index]; } value_t report_t::fn_is_seq(call_scope_t& scope) { return scope.value().is_sequence(); } value_t report_t::fn_strip(call_scope_t& args) { return args.value().strip_annotations(what_to_keep()); } value_t report_t::fn_trim(call_scope_t& args) { string temp(args.value().to_string()); scoped_array<char> buf(new char[temp.length() + 1]); std::strcpy(buf.get(), temp.c_str()); const char * p = buf.get(); const char * e = buf.get() + temp.length() - 1; while (p <= e && std::isspace(*p)) p++; while (e > p && std::isspace(*e)) e--; if (p > e) { return string_value(empty_string); } else { return string_value(string(p, static_cast<std::string::size_type>(e - p + 1))); } } value_t report_t::fn_format(call_scope_t& args) { format_t format(args.get<string>(0)); std::ostringstream out; out << format(args); return string_value(out.str()); } value_t report_t::fn_print(call_scope_t& args) { for (std::size_t i = 0; i < args.size(); i++) args[i].print(output_stream); static_cast<std::ostream&>(output_stream) << std::endl; return true; } value_t report_t::fn_scrub(call_scope_t& args) { return display_value(args.value()); } value_t report_t::fn_rounded(call_scope_t& args) { return args.value().rounded(); } value_t report_t::fn_unrounded(call_scope_t& args) { return args.value().unrounded(); } value_t report_t::fn_quantity(call_scope_t& args) { return args.get<amount_t>(0).number(); } value_t report_t::fn_floor(call_scope_t& args) { return args[0].floored(); } value_t report_t::fn_ceiling(call_scope_t& args) { return args[0].ceilinged(); } value_t report_t::fn_round(call_scope_t& args) { return args[0].rounded(); } value_t report_t::fn_roundto(call_scope_t& args) { return args[0].roundto(args.get<int>(1)); } value_t report_t::fn_unround(call_scope_t& args) { return args[0].unrounded(); } value_t report_t::fn_abs(call_scope_t& args) { return args[0].abs(); } value_t report_t::fn_truncated(call_scope_t& args) { return string_value(format_t::truncate (args.get<string>(0), (args.has<int>(1) && args.get<int>(1) > 0) ? static_cast<std::size_t>(args.get<int>(1)) : 0, args.has<int>(2) ? static_cast<std::size_t>(args.get<int>(2)) : 0)); } value_t report_t::fn_justify(call_scope_t& args) { uint_least8_t flags(AMOUNT_PRINT_ELIDE_COMMODITY_QUOTES); if (args.has<bool>(3) && args.get<bool>(3)) flags |= AMOUNT_PRINT_RIGHT_JUSTIFY; if (args.has<bool>(4) && args.get<bool>(4)) flags |= AMOUNT_PRINT_COLORIZE; std::ostringstream out; args[0].print(out, args.get<int>(1), args.has<int>(2) ? args.get<int>(2) : -1, flags); return string_value(out.str()); } value_t report_t::fn_quoted(call_scope_t& args) { std::ostringstream out; out << '"'; string arg(args.get<string>(0)); foreach (const char ch, arg) { if (ch == '"') out << "\\\""; else out << ch; } out << '"'; return string_value(out.str()); } value_t report_t::fn_quoted_rfc(call_scope_t& args) { std::ostringstream out; out << '"'; string arg(args.get<string>(0)); foreach (const char ch, arg) { if (ch == '"') out << '"' << '"'; else out << ch; } out << '"'; return string_value(out.str()); } value_t report_t::fn_join(call_scope_t& args) { std::ostringstream out; string arg(args.get<string>(0)); foreach (const char ch, arg) { if (ch != '\n') out << ch; else out << "\\n"; } return string_value(out.str()); } value_t report_t::fn_format_date(call_scope_t& args) { if (args.has<string>(1)) return string_value(format_date(args.get<date_t>(0), FMT_CUSTOM, args.get<string>(1).c_str())); else return string_value(format_date(args.get<date_t>(0), FMT_PRINTED)); } value_t report_t::fn_format_datetime(call_scope_t& args) { if (args.has<string>(1)) return string_value(format_datetime(args.get<datetime_t>(0), FMT_CUSTOM, args.get<string>(1).c_str())); else return string_value(format_datetime(args.get<datetime_t>(0), FMT_PRINTED)); } value_t report_t::fn_ansify_if(call_scope_t& args) { if (args.has<string>(1)) { string color = args.get<string>(1); std::ostringstream buf; if (color == "black") buf << "\033[30m"; else if (color == "red") buf << "\033[31m"; else if (color == "green") buf << "\033[32m"; else if (color == "yellow") buf << "\033[33m"; else if (color == "blue") buf << "\033[34m"; else if (color == "magenta") buf << "\033[35m"; else if (color == "cyan") buf << "\033[36m"; else if (color == "white") buf << "\033[37m"; else if (color == "bold") buf << "\033[1m"; else if (color == "underline") buf << "\033[4m"; else if (color == "blink") buf << "\033[5m"; buf << args[0]; buf << "\033[0m"; return string_value(buf.str()); } return args[0]; } value_t report_t::fn_percent(call_scope_t& args) { return (amount_t("100.00%") * (args.get<amount_t>(0) / args.get<amount_t>(1)).number()); } value_t report_t::fn_commodity(call_scope_t& args) { return string_value(args.get<amount_t>(0).commodity().symbol()); } value_t report_t::fn_commodity_price(call_scope_t& args) { optional<price_point_t> price_point = commodity_pool_t::current_pool->commodity_price_history.find_price (args.get<amount_t>(0).commodity(), args.get<datetime_t>(1)); if (price_point) { return price_point->price; } else { return amount_t(); } } value_t report_t::fn_set_commodity_price(call_scope_t& args) { args.get<amount_t>(0).commodity().add_price( args.get<datetime_t>(1), args.get<amount_t>(2), true); return NULL_VALUE; } value_t report_t::fn_clear_commodity(call_scope_t& args) { amount_t amt(args.get<amount_t>(0)); amt.clear_commodity(); return amt; } value_t report_t::fn_nail_down(call_scope_t& args) { value_t arg0(args[0]); value_t arg1(args[1]); switch (arg0.type()) { case value_t::AMOUNT: { amount_t tmp(arg0.as_amount()); if (tmp.has_commodity() && ! tmp.is_null() && ! tmp.is_realzero()) { arg1 = arg1.strip_annotations(keep_details_t()).to_amount(); expr_t value_expr(is_expr(arg1) ? as_expr(arg1) : expr_t::op_t::wrap_value(arg1.unrounded() / arg0.number())); std::ostringstream buf; value_expr.print(buf); value_expr.set_text(buf.str()); tmp.set_commodity(tmp.commodity().nail_down(value_expr)); } return tmp; } case value_t::BALANCE: { balance_t tmp; foreach (const balance_t::amounts_map::value_type& pair, arg0.as_balance_lval().amounts) { call_scope_t inner_args(*args.parent); inner_args.push_back(pair.second); inner_args.push_back(arg1); tmp += fn_nail_down(inner_args).as_amount(); } return tmp; } case value_t::SEQUENCE: { value_t tmp; foreach (value_t& value, arg0.as_sequence_lval()) { call_scope_t inner_args(*args.parent); inner_args.push_back(value); inner_args.push_back(arg1); tmp.push_back(fn_nail_down(inner_args)); } return tmp; } default: throw_(std::runtime_error, _f("Attempting to nail down %1%") % args[0].label()); } return arg0; } value_t report_t::fn_lot_date(call_scope_t& args) { if (args[0].has_annotation()) { const annotation_t& details(args[0].annotation()); if (details.date) return *details.date; } return NULL_VALUE; } value_t report_t::fn_lot_price(call_scope_t& args) { if (args[0].has_annotation()) { const annotation_t& details(args[0].annotation()); if (details.price) return *details.price; } return NULL_VALUE; } value_t report_t::fn_lot_tag(call_scope_t& args) { if (args[0].has_annotation()) { const annotation_t& details(args[0].annotation()); if (details.tag) return string_value(*details.tag); } return NULL_VALUE; } value_t report_t::fn_to_boolean(call_scope_t& args) { return args.get<bool>(0); } value_t report_t::fn_to_int(call_scope_t& args) { // This method is not called fn_to_long, because that would be // confusing to users who don't care about the distinction between // integer and long. return args.get<long>(0); } value_t report_t::fn_to_datetime(call_scope_t& args) { return args.get<datetime_t>(0); } value_t report_t::fn_to_date(call_scope_t& args) { return args.get<date_t>(0); } value_t report_t::fn_to_amount(call_scope_t& args) { return args.get<amount_t>(0); } value_t report_t::fn_to_balance(call_scope_t& args) { return args.get<balance_t>(0); } value_t report_t::fn_to_string(call_scope_t& args) { return string_value(args.get<string>(0)); } value_t report_t::fn_to_mask(call_scope_t& args) { return args.get<mask_t>(0); } value_t report_t::fn_to_sequence(call_scope_t& args) { return args[0].to_sequence(); } namespace { value_t fn_black(call_scope_t&) { return string_value("black"); } value_t fn_blink(call_scope_t&) { return string_value("blink"); } value_t fn_blue(call_scope_t&) { return string_value("blue"); } value_t fn_bold(call_scope_t&) { return string_value("bold"); } value_t fn_cyan(call_scope_t&) { return string_value("cyan"); } value_t fn_green(call_scope_t&) { return string_value("green"); } value_t fn_magenta(call_scope_t&) { return string_value("magenta"); } value_t fn_red(call_scope_t&) { return string_value("red"); } value_t fn_underline(call_scope_t&) { return string_value("underline"); } value_t fn_white(call_scope_t&) { return string_value("white"); } value_t fn_yellow(call_scope_t&) { return string_value("yellow"); } value_t fn_false(call_scope_t&) { return false; } value_t fn_null(call_scope_t&) { return NULL_VALUE; } } value_t report_t::reload_command(call_scope_t&) { session.close_journal_files(); session.read_journal_files(); return true; } value_t report_t::echo_command(call_scope_t& args) { std::ostream& out(output_stream); out << args.get<string>(0) << std::endl; return true; } value_t report_t::pricemap_command(call_scope_t& args) { std::ostream& out(output_stream); commodity_pool_t::current_pool->commodity_price_history.print_map (out, args.has<string>(0) ? datetime_t(parse_date(args.get<string>(0))) : datetime_t()); return true; } option_t<report_t> * report_t::lookup_option(const char * p) { switch (*p) { case '%': OPT_CH(percent); break; case 'A': OPT_CH(average); break; case 'B': OPT_CH(basis); break; case 'C': OPT_CH(cleared); break; case 'D': OPT_CH(daily); break; case 'E': OPT_CH(empty); break; case 'F': OPT_CH(format_); break; case 'G': OPT_CH(gain); break; case 'H': OPT_CH(historical); break; case 'I': OPT_CH(price); break; case 'J': OPT_CH(total_data); break; case 'L': OPT_CH(actual); break; case 'M': OPT_CH(monthly); break; case 'O': OPT_CH(quantity); break; case 'P': OPT_CH(by_payee); break; case 'R': OPT_CH(real); break; case 'S': OPT_CH(sort_); break; case 'T': OPT_CH(total_); break; case 'U': OPT_CH(uncleared); break; case 'V': OPT_CH(market); break; case 'W': OPT_CH(weekly); break; case 'X': OPT_CH(exchange_); break; case 'Y': OPT_CH(yearly); break; case 'a': OPT(abbrev_len_); else OPT_(account_); else OPT(actual); else OPT(add_budget); else OPT(amount_); else OPT(amount_data); else OPT_ALT(primary_date, actual_dates); else OPT(anon); else OPT_ALT(color, ansi); else OPT(auto_match); else OPT(aux_date); else OPT(average); else OPT(account_width_); else OPT(amount_width_); else OPT(average_lot_prices); break; case 'b': OPT(balance_format_); else OPT(base); else OPT(basis); else OPT_(begin_); else OPT(bold_if_); else OPT(budget); else OPT(budget_format_); else OPT(by_payee); break; case 'c': OPT(csv_format_); else OPT_ALT(gain, change); else OPT(cleared); else OPT(cleared_format_); else OPT(collapse); else OPT(collapse_if_zero); else OPT(color); else OPT(columns_); else OPT_ALT(basis, cost); else OPT_(current); else OPT(count); break; case 'd': OPT(daily); else OPT(date_); else OPT(date_format_); else OPT(datetime_format_); else OPT(dc); else OPT(depth_); else OPT(deviation); else OPT_ALT(rich_data, detail); else OPT_(display_); else OPT(display_amount_); else OPT(display_total_); else OPT_ALT(dow, days_of_week); else OPT(date_width_); break; case 'e': OPT(empty); else OPT_(end_); else OPT(equity); else OPT(exact); else OPT(exchange_); else OPT_ALT(aux_date, effective); break; case 'f': OPT(flat); else OPT_ALT(forecast_while_, forecast_); else OPT(forecast_years_); else OPT(format_); else OPT(force_color); else OPT(force_pager); else OPT_ALT(head_, first_); break; case 'g': OPT(gain); else OPT(group_by_); else OPT(group_title_format_); else OPT(generated); break; case 'h': OPT(head_); else OPT(historical); break; case 'i': OPT(invert); else OPT(inject_); else OPT(immediate); break; case 'j': OPT_CH(amount_data); break; case 'l': OPT_(limit_); else OPT(lot_dates); else OPT(lot_prices); else OPT_ALT(lot_notes, lot_tags); else OPT(lots); else OPT(lots_actual); else OPT_ALT(tail_, last_); break; case 'm': OPT(market); else OPT(monthly); else OPT(meta_); else OPT(meta_width_); break; case 'n': OPT_CH(collapse); else OPT(no_color); else OPT(no_pager); else OPT(no_revalued); else OPT(no_rounding); else OPT(no_titles); else OPT(no_total); else OPT(now_); break; case 'o': OPT(only_); else OPT_(output_); break; case 'p': OPT(pager_); else OPT(payee_); else OPT(pending); else OPT(percent); else OPT_(period_); else OPT_ALT(sort_xacts_, period_sort_); else OPT(pivot_); else OPT(plot_amount_format_); else OPT(plot_total_format_); else OPT(price); else OPT(prices_format_); else OPT(pricedb_format_); else OPT(primary_date); else OPT(payee_width_); else OPT(prepend_format_); else OPT(prepend_width_); break; case 'q': OPT(quantity); else OPT(quarterly); break; case 'r': OPT(raw); else OPT(real); else OPT(register_format_); else OPT_(related); else OPT(related_all); else OPT(revalued); else OPT(revalued_only); else OPT(revalued_total_); else OPT(rich_data); break; case 's': OPT(sort_); else OPT(sort_all_); else OPT(sort_xacts_); else OPT_(subtotal); else OPT(start_of_week_); else OPT(seed_); break; case 't': OPT_CH(amount_); else OPT(tail_); else OPT(total_); else OPT(total_data); else OPT(truncate_); else OPT(total_width_); else OPT(time_report); break; case 'u': OPT(unbudgeted); else OPT(uncleared); else OPT(unrealized); else OPT(unrealized_gains_); else OPT(unrealized_losses_); else OPT(unround); break; case 'v': OPT_ALT(market, value); else OPT(values); break; case 'w': OPT(weekly); else OPT_(wide); break; case 'y': OPT_CH(date_format_); else OPT(yearly); break; } return NULL; } void report_t::define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) { session.define(kind, name, def); } expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, const string& name) { if (expr_t::ptr_op_t def = session.lookup(kind, name)) return def; const char * p = name.c_str(); switch (kind) { case symbol_t::FUNCTION: // Support 2.x's single-letter value expression names. if (*(p + 1) == '\0') { switch (*p) { case 'd': case 'm': return MAKE_FUNCTOR(report_t::fn_now); case 'P': return MAKE_FUNCTOR(report_t::fn_market); case 't': return MAKE_FUNCTOR(report_t::fn_display_amount); case 'T': return MAKE_FUNCTOR(report_t::fn_display_total); case 'U': return MAKE_FUNCTOR(report_t::fn_abs); case 'S': return MAKE_FUNCTOR(report_t::fn_strip); case 'i': throw_(std::runtime_error, _("The i value expression variable is no longer supported")); case 'A': throw_(std::runtime_error, _("The A value expression variable is no longer supported")); case 'v': case 'V': throw_(std::runtime_error, _("The V and v value expression variables are no longer supported")); case 'I': case 'B': throw_(std::runtime_error, _("The I and B value expression variables are no longer supported")); case 'g': case 'G': throw_(std::runtime_error, _("The G and g value expression variables are no longer supported")); default: return NULL; } } switch (*p) { case 'a': if (is_eq(p, "amount_expr")) return MAKE_FUNCTOR(report_t::fn_amount_expr); else if (is_eq(p, "ansify_if")) return MAKE_FUNCTOR(report_t::fn_ansify_if); else if (is_eq(p, "abs")) return MAKE_FUNCTOR(report_t::fn_abs); else if (is_eq(p, "averaged_lots")) return MAKE_FUNCTOR(report_t::fn_averaged_lots); break; case 'b': if (is_eq(p, "black")) return WRAP_FUNCTOR(fn_black); else if (is_eq(p, "blink")) return WRAP_FUNCTOR(fn_blink); else if (is_eq(p, "blue")) return WRAP_FUNCTOR(fn_blue); else if (is_eq(p, "bold")) return WRAP_FUNCTOR(fn_bold); break; case 'c': if (is_eq(p, "cyan")) return WRAP_FUNCTOR(fn_cyan); else if (is_eq(p, "commodity")) return MAKE_FUNCTOR(report_t::fn_commodity); else if (is_eq(p, "commodity_price")) return MAKE_FUNCTOR(report_t::fn_commodity_price); else if (is_eq(p, "ceiling")) return MAKE_FUNCTOR(report_t::fn_ceiling); else if (is_eq(p, "clear_commodity")) return MAKE_FUNCTOR(report_t::fn_clear_commodity); break; case 'd': if (is_eq(p, "display_amount")) return MAKE_FUNCTOR(report_t::fn_display_amount); else if (is_eq(p, "display_total")) return MAKE_FUNCTOR(report_t::fn_display_total); else if (is_eq(p, "date")) return MAKE_FUNCTOR(report_t::fn_today); break; case 'f': if (is_eq(p, "format_date")) return MAKE_FUNCTOR(report_t::fn_format_date); else if (is_eq(p, "format_datetime")) return MAKE_FUNCTOR(report_t::fn_format_datetime); else if (is_eq(p, "format")) return MAKE_FUNCTOR(report_t::fn_format); else if (is_eq(p, "floor")) return MAKE_FUNCTOR(report_t::fn_floor); break; case 'g': if (is_eq(p, "get_at")) return MAKE_FUNCTOR(report_t::fn_get_at); else if (is_eq(p, "green")) return WRAP_FUNCTOR(fn_green); break; case 'i': if (is_eq(p, "is_seq")) return MAKE_FUNCTOR(report_t::fn_is_seq); break; case 'j': if (is_eq(p, "justify")) return MAKE_FUNCTOR(report_t::fn_justify); else if (is_eq(p, "join")) return MAKE_FUNCTOR(report_t::fn_join); break; case 'm': if (is_eq(p, "market")) return MAKE_FUNCTOR(report_t::fn_market); else if (is_eq(p, "magenta")) return WRAP_FUNCTOR(fn_magenta); break; case 'n': if (is_eq(p, "null")) return WRAP_FUNCTOR(fn_null); else if (is_eq(p, "now")) return MAKE_FUNCTOR(report_t::fn_now); else if (is_eq(p, "nail_down")) return MAKE_FUNCTOR(report_t::fn_nail_down); break; case 'o': if (is_eq(p, "options")) return MAKE_FUNCTOR(report_t::fn_options); break; case 'p': if (is_eq(p, "post")) return WRAP_FUNCTOR(fn_false); else if (is_eq(p, "percent")) return MAKE_FUNCTOR(report_t::fn_percent); else if (is_eq(p, "print")) return MAKE_FUNCTOR(report_t::fn_print); break; case 'q': if (is_eq(p, "quoted")) return MAKE_FUNCTOR(report_t::fn_quoted); else if (is_eq(p, "quoted_rfc")) return MAKE_FUNCTOR(report_t::fn_quoted_rfc); else if (is_eq(p, "quantity")) return MAKE_FUNCTOR(report_t::fn_quantity); break; case 'r': if (is_eq(p, "rounded")) return MAKE_FUNCTOR(report_t::fn_rounded); else if (is_eq(p, "red")) return WRAP_FUNCTOR(fn_red); else if (is_eq(p, "round")) return MAKE_FUNCTOR(report_t::fn_round); else if (is_eq(p, "roundto")) return MAKE_FUNCTOR(report_t::fn_roundto); break; case 's': if (is_eq(p, "scrub")) return MAKE_FUNCTOR(report_t::fn_scrub); else if (is_eq(p, "strip")) return MAKE_FUNCTOR(report_t::fn_strip); else if (is_eq(p, "should_bold")) return MAKE_FUNCTOR(report_t::fn_should_bold); else if (is_eq(p, "set_commodity_price")) return MAKE_FUNCTOR(report_t::fn_set_commodity_price); break; case 't': if (is_eq(p, "truncated")) return MAKE_FUNCTOR(report_t::fn_truncated); else if (is_eq(p, "total_expr")) return MAKE_FUNCTOR(report_t::fn_total_expr); else if (is_eq(p, "today")) return MAKE_FUNCTOR(report_t::fn_today); else if (is_eq(p, "t")) return MAKE_FUNCTOR(report_t::fn_display_amount); else if (is_eq(p, "trim")) return MAKE_FUNCTOR(report_t::fn_trim); else if (is_eq(p, "top_amount")) return MAKE_FUNCTOR(report_t::fn_top_amount); else if (is_eq(p, "to_boolean")) return MAKE_FUNCTOR(report_t::fn_to_boolean); else if (is_eq(p, "to_int")) return MAKE_FUNCTOR(report_t::fn_to_int); else if (is_eq(p, "to_datetime")) return MAKE_FUNCTOR(report_t::fn_to_datetime); else if (is_eq(p, "to_date")) return MAKE_FUNCTOR(report_t::fn_to_date); else if (is_eq(p, "to_amount")) return MAKE_FUNCTOR(report_t::fn_to_amount); else if (is_eq(p, "to_balance")) return MAKE_FUNCTOR(report_t::fn_to_balance); else if (is_eq(p, "to_string")) return MAKE_FUNCTOR(report_t::fn_to_string); else if (is_eq(p, "to_mask")) return MAKE_FUNCTOR(report_t::fn_to_mask); else if (is_eq(p, "to_sequence")) return MAKE_FUNCTOR(report_t::fn_to_sequence); break; case 'T': if (is_eq(p, "T")) return MAKE_FUNCTOR(report_t::fn_display_total); break; case 'u': if (is_eq(p, "underline")) return WRAP_FUNCTOR(fn_underline); else if (is_eq(p, "unround")) return MAKE_FUNCTOR(report_t::fn_unround); else if (is_eq(p, "unrounded")) return MAKE_FUNCTOR(report_t::fn_unrounded); break; case 'v': if (is_eq(p, "value_date")) return MAKE_FUNCTOR(report_t::fn_now); break; case 'w': if (is_eq(p, "white")) return WRAP_FUNCTOR(fn_white); break; case 'y': if (is_eq(p, "yellow")) return WRAP_FUNCTOR(fn_yellow); break; } // Check if they are trying to access an option's setting or value. if (option_t<report_t> * handler = lookup_option(p)) return MAKE_OPT_FUNCTOR(report_t, handler); break; case symbol_t::OPTION: if (option_t<report_t> * handler = lookup_option(p)) return MAKE_OPT_HANDLER(report_t, handler); break; #define POSTS_REPORTER(formatter) \ WRAP_FUNCTOR(reporter<>(post_handler_ptr(formatter), *this, \ string("#") + p)) // Can't use WRAP_FUNCTOR here because the template arguments // confuse the parser #define POSTS_REPORTER_(method, formatter) \ expr_t::op_t::wrap_functor \ (reporter<post_t, post_handler_ptr, method> \ (post_handler_ptr(formatter), *this, string("#") + p)) #define FORMATTED_POSTS_REPORTER(format) \ POSTS_REPORTER \ (new format_posts \ (*this, report_format(HANDLER(format)), \ maybe_format(HANDLER(prepend_format_)), \ HANDLED(prepend_width_) ? \ lexical_cast<std::size_t>(HANDLER(prepend_width_).str()) : 0)) #define FORMATTED_COMMODITIES_REPORTER(format) \ POSTS_REPORTER_ \ (&report_t::commodities_report, \ new format_posts \ (*this, report_format(HANDLER(format)), \ maybe_format(HANDLER(prepend_format_)), \ HANDLED(prepend_width_) ? \ lexical_cast<std::size_t>(HANDLER(prepend_width_).str()) : 0)) #define ACCOUNTS_REPORTER(formatter) \ expr_t::op_t::wrap_functor(reporter<account_t, acct_handler_ptr, \ &report_t::accounts_report> \ (acct_handler_ptr(formatter), *this, \ string("#") + p)) #define FORMATTED_ACCOUNTS_REPORTER(format) \ ACCOUNTS_REPORTER \ (new format_accounts \ (*this, report_format(HANDLER(format)), \ maybe_format(HANDLER(prepend_format_)), \ HANDLED(prepend_width_) ? \ lexical_cast<std::size_t>(HANDLER(prepend_width_).str()) : 0)) case symbol_t::COMMAND: switch (*p) { case 'a': if (is_eq(p, "accounts")) { return POSTS_REPORTER(new report_accounts(*this)); } break; case 'b': if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) { // jww (2023-01-27): This next 'if' statement is a hack for historical // purposes. Until this date, the balance report always used an amount // width of 20. If the user has set the amount width, this should be // used instead; but if they haven't, we need to use the old default // in order for the tests to pass. if (! HANDLED(amount_width_)) HANDLER(amount_width_).value = "20"; return FORMATTED_ACCOUNTS_REPORTER(balance_format_); } else if (is_eq(p, "budget")) { HANDLER(amount_).on(string("#budget"), "(amount, 0)"); budget_flags |= BUDGET_WRAP_VALUES; if (! (budget_flags & ~BUDGET_WRAP_VALUES)) budget_flags |= BUDGET_BUDGETED; return FORMATTED_ACCOUNTS_REPORTER(budget_format_); } break; case 'c': if (is_eq(p, "csv")) { return FORMATTED_POSTS_REPORTER(csv_format_); } else if (is_eq(p, "cleared")) { HANDLER(amount_).on(string("#cleared"), "(amount, cleared ? amount : 0)"); return FORMATTED_ACCOUNTS_REPORTER(cleared_format_); } else if (is_eq(p, "convert")) { return WRAP_FUNCTOR(convert_command); } else if (is_eq(p, "commodities")) { return POSTS_REPORTER(new report_commodities(*this)); } break; case 'd': if (is_eq(p, "draft")) { return WRAP_FUNCTOR(xact_command); } break; case 'e': if (is_eq(p, "equity")) { HANDLER(generated).on("#equity"); return POSTS_REPORTER(new print_xacts(*this)); } else if (is_eq(p, "entry")) { return WRAP_FUNCTOR(xact_command); } else if (is_eq(p, "emacs")) { return POSTS_REPORTER(new format_emacs_posts(output_stream)); } else if (is_eq(p, "echo")) { return MAKE_FUNCTOR(report_t::echo_command); } break; case 'l': if (is_eq(p, "lisp")) return POSTS_REPORTER(new format_emacs_posts(output_stream)); break; case 'p': if (*(p + 1) == '\0' || is_eq(p, "print")) { return POSTS_REPORTER(new print_xacts(*this, HANDLED(raw))); } else if (is_eq(p, "prices")) { return FORMATTED_COMMODITIES_REPORTER(prices_format_); } else if (is_eq(p, "pricedb") || is_eq(p, "pricesdb")) { return FORMATTED_COMMODITIES_REPORTER(pricedb_format_); } else if (is_eq(p, "pricemap")) { return MAKE_FUNCTOR(report_t::pricemap_command); } else if (is_eq(p, "payees")) { return POSTS_REPORTER(new report_payees(*this)); } break; case 'r': if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register")) { return FORMATTED_POSTS_REPORTER(register_format_); } else if (is_eq(p, "reload")) { return MAKE_FUNCTOR(report_t::reload_command); } break; case 's': if (is_eq(p, "stats") || is_eq(p, "stat")) return WRAP_FUNCTOR(report_statistics); else if (is_eq(p, "source")) return WRAP_FUNCTOR(source_command); else if (is_eq(p, "select")) return WRAP_FUNCTOR(select_command); break; case 't': if (is_eq(p, "tags")) { return POSTS_REPORTER(new report_tags(*this)); } break; case 'x': if (is_eq(p, "xact")) return WRAP_FUNCTOR(xact_command); else if (is_eq(p, "xml")) return POSTS_REPORTER(new format_ptree(*this, format_ptree::FORMAT_XML)); break; } break; case symbol_t::PRECOMMAND: switch (*p) { case 'a': if (is_eq(p, "args")) return WRAP_FUNCTOR(query_command); break; case 'e': if (is_eq(p, "eval")) return WRAP_FUNCTOR(eval_command); else if (is_eq(p, "expr")) return WRAP_FUNCTOR(parse_command); break; case 'f': if (is_eq(p, "format")) return WRAP_FUNCTOR(format_command); break; case 'g': if (is_eq(p, "generate")) return POSTS_REPORTER_(&report_t::generate_report, new print_xacts(*this)); break; case 'p': if (is_eq(p, "parse")) return WRAP_FUNCTOR(parse_command); else if (is_eq(p, "period")) return WRAP_FUNCTOR(period_command); break; case 'q': if (is_eq(p, "query")) return WRAP_FUNCTOR(query_command); break; case 's': if (is_eq(p, "script")) return WRAP_FUNCTOR(source_command); break; case 't': if (is_eq(p, "template")) return WRAP_FUNCTOR(template_command); break; } break; default: break; } return NULL; } } // namespace ledger �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/report.h���������������������������������������������������������������������������0000664�0000000�0000000�00000107151�14411236400�0015234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup report */ /** * @file report.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_REPORT_H #define INCLUDED_REPORT_H #include "expr.h" #include "query.h" #include "chain.h" #include "stream.h" #include "option.h" #include "commodity.h" #include "annotate.h" #include "session.h" #include "format.h" namespace ledger { class session_t; class xact_t; // These are the elements of any report: // // 1. Formatting string used for outputting the underlying ReportedType. // // 2. Handler object for the ReportedType. This is constructed using #1, or // else #1 is ignored completely. This handler object is also constructed // with the output stream that will be used during formatting. // // --- The details of #1 and #2 together represent the ItemHandler. // // 3. Mode of the report. Currently there are four modes: // // a. Posting or commodity iteration. In this mode, all the journal's // xacts, the postings of a specific xact, or all the journal's // commodities are walked. In the first two cases, it's the underlying // postings which are passed to #2; in the second case, each // commodity is passed to #2. // // b. Account iteration. This employs step 'a', but add a prologue and // epilogue to it. In the prologue it "sums" all account totals and // subtotals; in the epilogue it calls yet another handler whose job is // reporting (the handler used in 'a' is only for calculation). // // There is one variation on 'b' in which a "totals" line is also // displayed. // // c. Write journal. In this mode, a single function is called that output // the journal object as a textual file. #2 is used to print out each // posting in the journal. // // d. Dump binary file. This is just like 'c', except that it dumps out a // binary file and #2 is completely ignored. // // 4. For 'a' and 'b' in #3, there is a different iteration function called, // depending on whether we're iterating: // // a. The postings of an xact: walk_postings. // b. The xacts of a journal: walk_xacts. // c. The commodities of a journal: walk_commodities. // // 5. Finally, for the 'a' and 'b' reporting modes, there is a variant which // says that the formatter should be "flushed" after the entities are // iterated. This does not happen for the commodities iteration, however. class report_t : public scope_t { report_t(); public: session_t& session; output_stream_t output_stream; #define BUDGET_NO_BUDGET 0x00 #define BUDGET_BUDGETED 0x01 #define BUDGET_UNBUDGETED 0x02 #define BUDGET_WRAP_VALUES 0x04 datetime_t terminus; uint_least8_t budget_flags; explicit report_t(session_t& _session) : session(_session), terminus(CURRENT_TIME()), budget_flags(BUDGET_NO_BUDGET) { TRACE_CTOR(report_t, "session_t&"); } report_t(const report_t& report) : scope_t(report), session(report.session), output_stream(report.output_stream), terminus(report.terminus), budget_flags(report.budget_flags) { TRACE_CTOR(report_t, "copy"); } virtual ~report_t() { TRACE_DTOR(report_t); output_stream.close(); } void quick_close() { output_stream.close(); } virtual string description() { return _("current report"); } void normalize_options(const string& verb); void normalize_period(); void parse_query_args(const value_t& args, const string& whence); void posts_report(post_handler_ptr handler); void generate_report(post_handler_ptr handler); void xact_report(post_handler_ptr handler, xact_t& xact); void accounts_report(acct_handler_ptr handler); void commodities_report(post_handler_ptr handler); value_t display_value(const value_t& val); value_t fn_amount_expr(call_scope_t& scope); value_t fn_total_expr(call_scope_t& scope); value_t fn_display_amount(call_scope_t& scope); value_t fn_display_total(call_scope_t& scope); value_t fn_top_amount(call_scope_t& val); value_t fn_should_bold(call_scope_t& scope); value_t fn_market(call_scope_t& scope); value_t fn_get_at(call_scope_t& scope); value_t fn_is_seq(call_scope_t& scope); value_t fn_strip(call_scope_t& scope); value_t fn_trim(call_scope_t& scope); value_t fn_format(call_scope_t& scope); value_t fn_print(call_scope_t& scope); value_t fn_scrub(call_scope_t& scope); value_t fn_quantity(call_scope_t& scope); value_t fn_rounded(call_scope_t& scope); value_t fn_unrounded(call_scope_t& scope); value_t fn_truncated(call_scope_t& scope); value_t fn_floor(call_scope_t& scope); value_t fn_ceiling(call_scope_t& scope); value_t fn_clear_commodity(call_scope_t& scope); value_t fn_round(call_scope_t& scope); value_t fn_roundto(call_scope_t& scope); value_t fn_unround(call_scope_t& scope); value_t fn_abs(call_scope_t& scope); value_t fn_justify(call_scope_t& scope); value_t fn_quoted(call_scope_t& scope); value_t fn_quoted_rfc(call_scope_t& scope); value_t fn_join(call_scope_t& scope); value_t fn_format_date(call_scope_t& scope); value_t fn_format_datetime(call_scope_t& scope); value_t fn_ansify_if(call_scope_t& scope); value_t fn_percent(call_scope_t& scope); value_t fn_commodity(call_scope_t& scope); value_t fn_commodity_price(call_scope_t& scope); value_t fn_set_commodity_price(call_scope_t& scope); value_t fn_nail_down(call_scope_t& scope); value_t fn_lot_date(call_scope_t& scope); value_t fn_lot_price(call_scope_t& scope); value_t fn_lot_tag(call_scope_t& scope); value_t fn_to_boolean(call_scope_t& scope); value_t fn_to_int(call_scope_t& scope); value_t fn_to_datetime(call_scope_t& scope); value_t fn_to_date(call_scope_t& scope); value_t fn_to_amount(call_scope_t& scope); value_t fn_to_balance(call_scope_t& scope); value_t fn_to_string(call_scope_t& scope); value_t fn_to_mask(call_scope_t& scope); value_t fn_to_sequence(call_scope_t& scope); value_t fn_averaged_lots(call_scope_t& scope); value_t fn_now(call_scope_t&) { return terminus; } value_t fn_today(call_scope_t&) { return terminus.date(); } value_t fn_options(call_scope_t&) { return scope_value(this); } string report_format(option_t<report_t>& option) { if (HANDLED(format_)) return HANDLER(format_).str(); return option.str(); } optional<string> maybe_format(option_t<report_t>& option) { if (option) return option.str(); return none; } value_t reload_command(call_scope_t&); value_t echo_command(call_scope_t& scope); value_t pricemap_command(call_scope_t& scope); keep_details_t what_to_keep() { bool lots = HANDLED(lots) || HANDLED(lots_actual); return keep_details_t(lots || HANDLED(lot_prices), lots || HANDLED(lot_dates), lots || HANDLED(lot_notes), HANDLED(lots_actual)); } void report_options(std::ostream& out) { HANDLER(abbrev_len_).report(out); HANDLER(account_).report(out); HANDLER(actual).report(out); HANDLER(add_budget).report(out); HANDLER(amount_).report(out); HANDLER(amount_data).report(out); HANDLER(anon).report(out); HANDLER(auto_match).report(out); HANDLER(aux_date).report(out); HANDLER(average).report(out); HANDLER(balance_format_).report(out); HANDLER(base).report(out); HANDLER(basis).report(out); HANDLER(begin_).report(out); HANDLER(budget).report(out); HANDLER(budget_format_).report(out); HANDLER(by_payee).report(out); HANDLER(cleared).report(out); HANDLER(cleared_format_).report(out); HANDLER(color).report(out); HANDLER(collapse).report(out); HANDLER(collapse_if_zero).report(out); HANDLER(columns_).report(out); HANDLER(csv_format_).report(out); HANDLER(current).report(out); HANDLER(daily).report(out); HANDLER(date_).report(out); HANDLER(date_format_).report(out); HANDLER(datetime_format_).report(out); HANDLER(dc).report(out); HANDLER(depth_).report(out); HANDLER(deviation).report(out); HANDLER(display_).report(out); HANDLER(display_amount_).report(out); HANDLER(display_total_).report(out); HANDLER(dow).report(out); HANDLER(empty).report(out); HANDLER(end_).report(out); HANDLER(equity).report(out); HANDLER(exact).report(out); HANDLER(exchange_).report(out); HANDLER(flat).report(out); HANDLER(force_color).report(out); HANDLER(force_pager).report(out); HANDLER(forecast_while_).report(out); HANDLER(forecast_years_).report(out); HANDLER(format_).report(out); HANDLER(gain).report(out); HANDLER(generated).report(out); HANDLER(group_by_).report(out); HANDLER(group_title_format_).report(out); HANDLER(head_).report(out); HANDLER(immediate).report(out); HANDLER(inject_).report(out); HANDLER(invert).report(out); HANDLER(limit_).report(out); HANDLER(lot_dates).report(out); HANDLER(lot_prices).report(out); HANDLER(average_lot_prices).report(out); HANDLER(lot_notes).report(out); HANDLER(lots).report(out); HANDLER(lots_actual).report(out); HANDLER(market).report(out); HANDLER(meta_).report(out); HANDLER(monthly).report(out); HANDLER(no_pager).report(out); HANDLER(no_rounding).report(out); HANDLER(no_titles).report(out); HANDLER(no_total).report(out); HANDLER(now_).report(out); HANDLER(only_).report(out); HANDLER(output_).report(out); HANDLER(pager_).report(out); HANDLER(payee_).report(out); HANDLER(pending).report(out); HANDLER(percent).report(out); HANDLER(period_).report(out); HANDLER(pivot_).report(out); HANDLER(plot_amount_format_).report(out); HANDLER(plot_total_format_).report(out); HANDLER(prepend_format_).report(out); HANDLER(prepend_width_).report(out); HANDLER(price).report(out); HANDLER(prices_format_).report(out); HANDLER(pricedb_format_).report(out); HANDLER(primary_date).report(out); HANDLER(quantity).report(out); HANDLER(quarterly).report(out); HANDLER(raw).report(out); HANDLER(real).report(out); HANDLER(register_format_).report(out); HANDLER(related).report(out); HANDLER(related_all).report(out); HANDLER(revalued).report(out); HANDLER(revalued_only).report(out); HANDLER(revalued_total_).report(out); HANDLER(rich_data).report(out); HANDLER(seed_).report(out); HANDLER(sort_).report(out); HANDLER(sort_all_).report(out); HANDLER(sort_xacts_).report(out); HANDLER(start_of_week_).report(out); HANDLER(subtotal).report(out); HANDLER(tail_).report(out); HANDLER(time_report).report(out); HANDLER(total_).report(out); HANDLER(total_data).report(out); HANDLER(truncate_).report(out); HANDLER(unbudgeted).report(out); HANDLER(uncleared).report(out); HANDLER(unrealized).report(out); HANDLER(unrealized_gains_).report(out); HANDLER(unrealized_losses_).report(out); HANDLER(unround).report(out); HANDLER(weekly).report(out); HANDLER(wide).report(out); HANDLER(yearly).report(out); HANDLER(meta_width_).report(out); HANDLER(date_width_).report(out); HANDLER(payee_width_).report(out); HANDLER(account_width_).report(out); HANDLER(amount_width_).report(out); HANDLER(total_width_).report(out); HANDLER(values).report(out); } option_t<report_t> * lookup_option(const char * p); virtual void define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); /** * Option handlers */ OPTION__ (report_t, abbrev_len_, CTOR(report_t, abbrev_len_) { on(none, "2"); }); OPTION(report_t, account_); OPTION_(report_t, actual, DO() { // -L OTHER(limit_).on(whence, "actual"); }); OPTION_(report_t, add_budget, DO() { parent->budget_flags |= BUDGET_BUDGETED | BUDGET_UNBUDGETED; }); OPTION__ (report_t, amount_, // -t DECL1(report_t, amount_, merged_expr_t, expr, ("amount_expr", "amount")) {} DO_(str) { expr.append(str); }); OPTION(report_t, amount_data); // -j OPTION(report_t, anon); OPTION(report_t, auto_match); OPTION_(report_t, average, DO() { // -A OTHER(empty).on(whence); OTHER(display_total_) .on(whence, "count>0?(display_total/count):0"); }); OPTION__ (report_t, balance_format_, CTOR(report_t, balance_format_) { on(none, "%(ansify_if(" " justify(scrub(display_total), max(int(amount_width),20)," " max(int(amount_width),20) + int(prepend_width), true, color)," " bold if should_bold))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(" " ansify_if(partial_account(options.flat), blue if color)," " bold if should_bold))\n%/" "%$1\n%/" "%(prepend_width ? \" \" * int(prepend_width) : \"\")" "%(\"-\" * max(int(amount_width),20))\n"); }); OPTION(report_t, base); OPTION_(report_t, basis, DO() { // -B OTHER(revalued).on(whence); OTHER(amount_).expr.set_base_expr("rounded(cost)"); }); OPTION_(report_t, begin_, DO_(str) { // -b date_interval_t interval(str); if (optional<date_t> begin = interval.begin()) { string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; OTHER(limit_).on(whence, predicate); } else { throw_(std::invalid_argument, _f("Could not determine beginning of period '%1%'") % str); } }); OPTION_ (report_t, bold_if_, expr_t expr; DO_(str) { expr = str; }); OPTION_(report_t, budget, DO() { parent->budget_flags |= BUDGET_BUDGETED; }); OPTION__ (report_t, budget_format_, CTOR(report_t, budget_format_) { on(none, "%(justify(scrub(get_at(display_total, 0)), int(amount_width), -1, true, color))" " %(justify(-scrub(get_at(display_total, 1)), int(amount_width), " " int(amount_width) + 1 + int(amount_width), true, color))" " %(justify(scrub((get_at(display_total, 1) || 0) + " " (get_at(display_total, 0) || 0)), int(amount_width), " " int(amount_width) + 1 + int(amount_width) + 1 + int(amount_width), true, color))" " %(ansify_if(" " justify((get_at(display_total, 1) ? " " (100% * quantity(scrub(get_at(display_total, 0)))) / " " -quantity(scrub(get_at(display_total, 1))) : 0), " " 5, -1, true, false)," " magenta if (color and get_at(display_total, 1) and " " (abs(quantity(scrub(get_at(display_total, 0))) / " " quantity(scrub(get_at(display_total, 1)))) >= 1))))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n" "%/%$1 %$2 %$3 %$4\n%/" "%(prepend_width ? \" \" * int(prepend_width) : \"\")" "------------ ------------ ------------ -----\n"); }); OPTION(report_t, by_payee); // -P OPTION_(report_t, cleared, DO() { // -C OTHER(limit_).on(whence, "cleared"); }); OPTION__ (report_t, cleared_format_, CTOR(report_t, cleared_format_) { on(none, "%(justify(scrub(get_at(display_total, 0)), 16, 16 + int(prepend_width), " " true, color)) %(justify(scrub(get_at(display_total, 1)), 18, " " 36 + int(prepend_width), true, color))" " %(latest_cleared ? format_date(latest_cleared) : \" \")" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" "%$1 %$2 %$3\n%/" "%(prepend_width ? \" \" * int(prepend_width) : \"\")" "---------------- ---------------- ---------\n"); }); OPTION(report_t, color); OPTION_(report_t, collapse, DO() { // -n // Make sure that balance reports are collapsed too, but only apply it // to account xacts OTHER(display_).on(whence, "post|depth<=1"); }); OPTION_(report_t, collapse_if_zero, DO() { OTHER(collapse).on(whence); }); OPTION(report_t, columns_); OPTION(report_t, count); OPTION__ (report_t, csv_format_, CTOR(report_t, csv_format_) { on(none, "%(quoted(date))," "%(quoted(code))," "%(quoted(payee))," "%(quoted(display_account))," "%(quoted(commodity(scrub(display_amount))))," "%(quoted(quantity(scrub(display_amount))))," "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," "%(quoted(join(note | xact.note)))\n"); }); OPTION_(report_t, current, DO() { // -c OTHER(limit_).on(whence, "date<=today"); }); OPTION_(report_t, daily, DO() { // -D OTHER(period_).on(whence, "daily"); }); OPTION(report_t, date_); OPTION(report_t, date_format_); OPTION(report_t, datetime_format_); OPTION_(report_t, dc, DO() { OTHER(amount_).expr.set_base_expr ("(amount > 0 ? amount : 0, amount < 0 ? amount : 0)"); OTHER(register_format_) .on(none, "%(ansify_if(" " ansify_if(justify(format_date(date), int(date_width))," " green if color and date > today)," " bold if should_bold))" " %(ansify_if(" " ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), " " bold if color and !cleared and actual)," " bold if should_bold))" " %(ansify_if(" " ansify_if(justify(truncated(display_account, int(account_width), " " int(abbrev_len)), int(account_width))," " blue if color)," " bold if should_bold))" " %(ansify_if(" " justify(scrub(abs(get_at(display_amount, 0))), int(amount_width), " " 3 + int(meta_width) + int(date_width) + int(payee_width)" " + int(account_width) + int(amount_width) + int(prepend_width)," " true, color)," " bold if should_bold))" " %(ansify_if(" " justify(scrub(abs(get_at(display_amount, 1))), int(amount_width), " " 4 + int(meta_width) + int(date_width) + int(payee_width)" " + int(account_width) + int(amount_width) + int(amount_width) + int(prepend_width)," " true, color)," " bold if should_bold))" " %(ansify_if(" " justify(scrub(get_at(display_total, 0) + get_at(display_total, 1)), int(total_width), " " 5 + int(meta_width) + int(date_width) + int(payee_width)" " + int(account_width) + int(amount_width) + int(amount_width) + int(total_width)" " + int(prepend_width), true, color)," " bold if should_bold))\n%/" "%(justify(\" \", int(date_width)))" " %(ansify_if(" " justify(truncated(has_tag(\"Payee\") ? payee : \" \", " " int(payee_width)), int(payee_width))," " bold if should_bold))" " %$3 %$4 %$5 %$6\n"); OTHER(balance_format_) .on(none, "%(ansify_if(" " justify(scrub(abs(get_at(display_total, 0))), 14," " 14 + int(prepend_width), true, color)," " bold if should_bold)) " "%(ansify_if(" " justify(scrub(abs(get_at(display_total, 1))), 14," " 14 + 1 + int(prepend_width) + int(total_width), true, color)," " bold if should_bold)) " "%(ansify_if(" " justify(scrub(get_at(display_total, 0) + get_at(display_total, 1)), 14," " 14 + 2 + int(prepend_width) + int(total_width) + int(total_width), true, color)," " bold if should_bold))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(" " ansify_if(partial_account(options.flat), blue if color)," " bold if should_bold))\n%/" "%$1 %$2 %$3\n%/" "%(prepend_width ? \" \" * int(prepend_width) : \"\")" "--------------------------------------------\n"); }); OPTION_(report_t, depth_, DO_(str) { OTHER(display_).on(whence, string("depth<=") + str); }); OPTION_(report_t, deviation, DO() { OTHER(display_total_) .on(whence, "display_amount-display_total"); }); OPTION_ (report_t, display_, DO_(str) { // -d if (handled) value = string("(") + value + ")&(" + str + ")"; }); OPTION__ (report_t, display_amount_, DECL1(report_t, display_amount_, merged_expr_t, expr, ("display_amount", "amount_expr")) {} DO_(str) { expr.append(str); }); OPTION__ (report_t, display_total_, DECL1(report_t, display_total_, merged_expr_t, expr, ("display_total", "total_expr")) {} DO_(str) { expr.append(str); }); OPTION(report_t, dow); OPTION(report_t, aux_date); OPTION(report_t, empty); // -E OPTION_(report_t, end_, DO_(str) { // -e // Use begin() here so that if the user says --end=2008, we end on // 2008/01/01 instead of 2009/01/01 (which is what end() would // return). date_interval_t interval(str); if (optional<date_t> end = interval.begin()) { string predicate = "date<[" + to_iso_extended_string(*end) + "]"; OTHER(limit_).on(whence, predicate); parent->terminus = datetime_t(*end); } else { throw_(std::invalid_argument, _f("Could not determine end of period '%1%'") % str); } }); OPTION(report_t, equity); OPTION(report_t, exact); OPTION_(report_t, exchange_, DO_(str) { // -X // Using -X implies -V. The main difference is that now // HANDLER(exchange_) contains the name of a commodity, which // is accessed via the "exchange" value expression function. OTHER(market).on(whence); }); OPTION(report_t, flat); OPTION(report_t, force_color); OPTION(report_t, force_pager); OPTION(report_t, forecast_while_); OPTION(report_t, forecast_years_); OPTION(report_t, format_); // -F OPTION_(report_t, gain, DO() { // -G OTHER(revalued).on(whence); OTHER(amount_).expr.set_base_expr("(amount, cost)"); // Since we are displaying the amounts of revalued postings, they // will end up being composite totals, and hence a pair of pairs. OTHER(display_amount_) .on(whence, "use_direct_amount ? amount :" " (is_seq(get_at(amount_expr, 0)) ?" " get_at(get_at(amount_expr, 0), 0) :" " market(get_at(amount_expr, 0), value_date, exchange)" " - get_at(amount_expr, 1))"); OTHER(revalued_total_) .on(whence, "(market(get_at(total_expr, 0), value_date, exchange), " "get_at(total_expr, 1))"); OTHER(display_total_) .on(whence, "use_direct_amount ? total_expr :" " market(get_at(total_expr, 0), value_date, exchange)" " - get_at(total_expr, 1)"); }); OPTION(report_t, generated); OPTION_ (report_t, group_by_, expr_t expr; DO_(str) { expr = str; }); OPTION__ (report_t, group_title_format_, CTOR(report_t, group_title_format_) { on(none, "%(value)\n"); }); OPTION(report_t, head_); OPTION_(report_t, historical, DO() { // -H OTHER(market).on(whence); OTHER(amount_) .on(whence, "nail_down(amount_expr, " "market(amount_expr, value_date, exchange))"); }); OPTION(report_t, immediate); OPTION(report_t, inject_); OPTION_(report_t, invert, DO() { OTHER(amount_).on(whence, "-amount_expr"); }); OPTION_ (report_t, limit_, DO_(str) { // -l if (handled) value = string("(") + value + ")&(" + str + ")"; }); OPTION(report_t, lot_dates); OPTION(report_t, lot_prices); OPTION_(report_t, average_lot_prices, DO() { OTHER(lot_prices).on(whence); OTHER(display_amount_) .on(whence, "averaged_lots(display_amount)"); OTHER(display_total_) .on(whence, "averaged_lots(display_total)"); }); OPTION(report_t, lot_notes); OPTION(report_t, lots); OPTION(report_t, lots_actual); OPTION_(report_t, market, DO() { // -V OTHER(revalued).on(whence); OTHER(display_amount_) .on(whence, "market(display_amount, value_date, exchange)"); OTHER(display_total_) .on(whence, "market(display_total, value_date, exchange)"); }); OPTION(report_t, meta_); OPTION_(report_t, monthly, DO() { // -M OTHER(period_).on(whence, "monthly"); }); OPTION_(report_t, no_color, DO() { OTHER(color).off(); }); OPTION_(report_t, no_revalued, DO() { OTHER(revalued).off(); }); OPTION(report_t, no_rounding); OPTION(report_t, no_titles); OPTION(report_t, no_total); OPTION_(report_t, now_, DO_(str) { date_interval_t interval(str); if (optional<date_t> begin = interval.begin()) { ledger::epoch = parent->terminus = datetime_t(*begin); } else { throw_(std::invalid_argument, _f("Could not determine beginning of period '%1%'") % str); } }); OPTION_ (report_t, only_, DO_(str) { if (handled) value = string("(") + value + ")&(" + str + ")"; }); OPTION(report_t, output_); // -o // setenv() is not available on WIN32 #if defined(HAVE_ISATTY) and !defined(_WIN32) and !defined(__CYGWIN__) OPTION__ (report_t, pager_, CTOR(report_t, pager_) { if (isatty(STDOUT_FILENO)) { if (! std::getenv("PAGER")) { bool have_less = false; if (exists(path("/opt/local/bin/less")) || exists(path("/usr/local/bin/less")) || exists(path("/usr/bin/less"))) have_less = true; if (have_less) { on(none, "less"); setenv("LESS", "-FRSX", 0); // don't overwrite } } else { on(none, std::getenv("PAGER")); setenv("LESS", "-FRSX", 0); // don't overwrite } } }); #else // HAVE_ISATTY OPTION(report_t, pager_); #endif // HAVE_ISATTY OPTION_(report_t, no_pager, DO() { OTHER(pager_).off(); }); OPTION(report_t, payee_); OPTION_(report_t, pending, DO() { // -C OTHER(limit_).on(whence, "pending"); }); OPTION_(report_t, percent, DO() { // -% OTHER(total_) .on(whence, "((is_account&parent&parent.total)?" " percent(scrub(total), scrub(parent.total)):0)"); }); OPTION_ (report_t, period_, DO_(str) { // -p if (handled) value += string(" ") + str; }); OPTION(report_t, pivot_); OPTION__ (report_t, plot_amount_format_, CTOR(report_t, plot_amount_format_) { on(none, "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n"); }); OPTION__ (report_t, plot_total_format_, CTOR(report_t, plot_total_format_) { on(none, "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n"); }); OPTION(report_t, prepend_format_); OPTION(report_t, prepend_width_); OPTION_(report_t, price, DO() { // -I OTHER(amount_).expr.set_base_expr("price"); }); OPTION__ (report_t, prices_format_, CTOR(report_t, prices_format_) { on(none, "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, " " 2 + 9 + 8 + 12, true, color))\n"); }); OPTION__ (report_t, pricedb_format_, CTOR(report_t, pricedb_format_) { on(none, "P %(datetime) %(display_account) %(scrub(display_amount))\n"); }); OPTION(report_t, primary_date); OPTION_(report_t, quantity, DO() { // -O OTHER(revalued).off(); OTHER(amount_).expr.set_base_expr("amount"); OTHER(total_).expr.set_base_expr("total"); }); OPTION_(report_t, quarterly, DO() { OTHER(period_).on(whence, "quarterly"); }); OPTION(report_t, raw); OPTION_(report_t, real, DO() { // -R OTHER(limit_).on(whence, "real"); }); OPTION__ (report_t, register_format_, CTOR(report_t, register_format_) { on(none, "%(ansify_if(" " ansify_if(justify(format_date(date), int(date_width))," " green if color and date > today)," " bold if should_bold))" " %(ansify_if(" " ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), " " bold if color and !cleared and actual)," " bold if should_bold))" " %(ansify_if(" " ansify_if(justify(truncated(display_account, int(account_width), " " int(abbrev_len)), int(account_width))," " blue if color)," " bold if should_bold))" " %(ansify_if(" " justify(scrub(display_amount), int(amount_width), " " 3 + int(meta_width) + int(date_width) + int(payee_width)" " + int(account_width) + int(amount_width) + int(prepend_width)," " true, color)," " bold if should_bold))" " %(ansify_if(" " justify(scrub(display_total), int(total_width), " " 4 + int(meta_width) + int(date_width) + int(payee_width)" " + int(account_width) + int(amount_width) + int(total_width)" " + int(prepend_width), true, color)," " bold if should_bold))\n%/" "%(justify(\" \", int(date_width)))" " %(ansify_if(" " justify(truncated(has_tag(\"Payee\") ? payee : \" \", " " int(payee_width)), int(payee_width))," " bold if should_bold))" " %$3 %$4 %$5\n"); }); OPTION(report_t, related); // -r OPTION_(report_t, related_all, DO() { OTHER(related).on(whence); }); OPTION(report_t, revalued); OPTION(report_t, revalued_only); OPTION_ (report_t, revalued_total_, expr_t expr; DO_(str) { expr = str; }); OPTION(report_t, rich_data); OPTION(report_t, seed_); OPTION_(report_t, sort_, DO_(str) { // -S OTHER(sort_xacts_).off(); OTHER(sort_all_).off(); }); OPTION_(report_t, sort_all_, DO_(str) { OTHER(sort_).on(whence, str); OTHER(sort_xacts_).off(); }); OPTION_(report_t, sort_xacts_, DO_(str) { OTHER(sort_).on(whence, str); OTHER(sort_all_).off(); }); OPTION(report_t, start_of_week_); OPTION(report_t, subtotal); // -s OPTION(report_t, tail_); OPTION_(report_t, time_report, DO() { OTHER(balance_format_) .on(none, "%(ansify_if(justify(earliest_checkin ? " " format_datetime(earliest_checkin) : \"\", 19, -1, true)," " bold if latest_checkout_cleared)) " "%(ansify_if(justify(latest_checkout ? " " format_datetime(latest_checkout) : \"\", 19, -1, true), " " bold if latest_checkout_cleared)) " "%(latest_checkout_cleared ? \"*\" : \" \") " "%(ansify_if(" " justify(scrub(display_total), 8," " 8 + 4 + 19 * 2, true, color), bold if should_bold))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(" " ansify_if(partial_account(options.flat), blue if color)," " bold if should_bold))\n%/" "%$1 %$2 %$3\n%/" "%(prepend_width ? \" \" * int(prepend_width) : \"\")" "--------------------------------------------------\n"); }); OPTION__ (report_t, total_, // -T DECL1(report_t, total_, merged_expr_t, expr, ("total_expr", "total")) {} DO_(str) { expr.append(str); }); OPTION(report_t, total_data); // -J OPTION_(report_t, truncate_, DO_(style) { if (style == "leading") format_t::default_style = format_t::TRUNCATE_LEADING; else if (style == "middle") format_t::default_style = format_t::TRUNCATE_MIDDLE; else if (style == "trailing") format_t::default_style = format_t::TRUNCATE_TRAILING; else throw_(std::invalid_argument, _f("Unrecognized truncation style: '%1%'") % style); format_t::default_style_changed = true; }); OPTION_(report_t, unbudgeted, DO() { parent->budget_flags |= BUDGET_UNBUDGETED; }); OPTION_(report_t, uncleared, DO() { // -U OTHER(limit_).on(whence, "uncleared|pending"); }); OPTION(report_t, unrealized); OPTION(report_t, unrealized_gains_); OPTION(report_t, unrealized_losses_); OPTION_(report_t, unround, DO() { OTHER(amount_).on(whence, "unrounded(amount_expr)"); OTHER(total_).on(whence, "unrounded(total_expr)"); }); OPTION_(report_t, weekly, DO() { // -W OTHER(period_).on(whence, "weekly"); }); OPTION_(report_t, wide, DO() { // -w OTHER(columns_).on(whence, "132"); }); OPTION_(report_t, yearly, DO() { // -Y OTHER(period_).on(whence, "yearly"); }); OPTION(report_t, meta_width_); OPTION(report_t, date_width_); OPTION(report_t, payee_width_); OPTION(report_t, account_width_); OPTION(report_t, amount_width_); OPTION(report_t, total_width_); OPTION(report_t, values); }; template <class Type = post_t, class handler_ptr = post_handler_ptr, void (report_t::*report_method)(handler_ptr) = &report_t::posts_report> class reporter { shared_ptr<item_handler<Type> > handler; report_t& report; string whence; public: reporter(shared_ptr<item_handler<Type> > _handler, report_t& _report, const string& _whence) : handler(_handler), report(_report), whence(_whence) { TRACE_CTOR(reporter, "item_handler<Type>, report_t&, string"); } reporter(const reporter& other) : handler(other.handler), report(other.report), whence(other.whence) { TRACE_CTOR(reporter, "copy"); } ~reporter() throw() { TRACE_DTOR(reporter); } value_t operator()(call_scope_t& args) { if (args.size() > 0) report.parse_query_args(args.value(), whence); (report.*report_method)(handler_ptr(handler)); return true; } }; } // namespace ledger #endif // INCLUDED_REPORT_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/scope.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000007106�14411236400�0015167�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "scope.h" namespace ledger { scope_t * scope_t::default_scope = NULL; empty_scope_t * scope_t::empty_scope = NULL; void symbol_scope_t::define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) { DEBUG("scope.symbols", "Defining '" << name << "' = " << def << " in " << this); if (! symbols) symbols = symbol_map(); std::pair<symbol_map::iterator, bool> result = symbols->insert(symbol_map::value_type(symbol_t(kind, name, def), def)); if (! result.second) { symbol_map::iterator i = symbols->find(symbol_t(kind, name)); assert(i != symbols->end()); symbols->erase(i); result = symbols->insert(symbol_map::value_type (symbol_t(kind, name, def), def)); if (! result.second) throw_(compile_error, _f("Redefinition of '%1%' in the same scope") % name); } } expr_t::ptr_op_t symbol_scope_t::lookup(const symbol_t::kind_t kind, const string& name) { if (symbols) { DEBUG("scope.symbols", "Looking for '" << name << "' in " << this); symbol_map::const_iterator i = symbols->find(symbol_t(kind, name)); if (i != symbols->end()) { DEBUG("scope.symbols", "Found '" << name << "' in " << this); return (*i).second; } } return child_scope_t::lookup(kind, name); } value_t& call_scope_t::resolve(const std::size_t index, value_t::type_t context, const bool required) { if (index >= args.size()) throw_(calc_error, _("Too few arguments to function")); value_t& value(args[index]); if (value.is_any()) { context_scope_t scope(*this, context, required); value = as_expr(value)->calc(scope, locus, depth); if (required && ! value.is_type(context)) throw_(calc_error, _f("Expected %1% for argument %2%, but received %3%") % value.label(context) % index % value.label()); } return value; } } // namespace ledger ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/scope.h����������������������������������������������������������������������������0000664�0000000�0000000�00000040033�14411236400�0015025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup expr */ /** * @file scope.h * @author John Wiegley * * @ingroup expr */ #ifndef INCLUDED_SCOPE_H #define INCLUDED_SCOPE_H #include "op.h" namespace ledger { struct symbol_t { enum kind_t { UNKNOWN, FUNCTION, OPTION, PRECOMMAND, COMMAND, DIRECTIVE, FORMAT }; kind_t kind; string name; expr_t::ptr_op_t definition; symbol_t() : kind(UNKNOWN), name(""), definition(NULL) { TRACE_CTOR(symbol_t, ""); } symbol_t(kind_t _kind, string _name, expr_t::ptr_op_t _definition = NULL) : kind(_kind), name(_name), definition(_definition) { TRACE_CTOR(symbol_t, "symbol_t::kind_t, string"); } symbol_t(const symbol_t& sym) : kind(sym.kind), name(sym.name), definition(sym.definition) { TRACE_CTOR(symbol_t, "copy"); } ~symbol_t() throw() { TRACE_DTOR(symbol_t); } bool operator<(const symbol_t& sym) const { return kind < sym.kind || name < sym.name; } bool operator==(const symbol_t& sym) const { return kind == sym.kind || name == sym.name; } }; class empty_scope_t; class scope_t { public: static scope_t * default_scope; static empty_scope_t * empty_scope; explicit scope_t() { TRACE_CTOR(scope_t, ""); } virtual ~scope_t() { TRACE_DTOR(scope_t); } virtual string description() = 0; virtual void define(const symbol_t::kind_t, const string&, expr_t::ptr_op_t) {} virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name) = 0; virtual value_t::type_t type_context() const { return value_t::VOID; } virtual bool type_required() const { return false; } }; class empty_scope_t : public scope_t { public: empty_scope_t() { TRACE_CTOR(empty_scope_t, ""); } ~empty_scope_t() throw() { TRACE_DTOR(empty_scope_t); } virtual string description() { return _("<empty>"); } virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t, const string&) { return NULL; } }; class child_scope_t : public noncopyable, public scope_t { public: scope_t * parent; explicit child_scope_t() : parent(NULL) { TRACE_CTOR(child_scope_t, ""); } explicit child_scope_t(scope_t& _parent) : parent(&_parent) { TRACE_CTOR(child_scope_t, "scope_t&"); } virtual ~child_scope_t() { TRACE_DTOR(child_scope_t); } virtual void define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) { if (parent) parent->define(kind, name, def); } virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name) { if (parent) return parent->lookup(kind, name); return NULL; } }; class bind_scope_t : public child_scope_t { bind_scope_t(); public: scope_t& grandchild; explicit bind_scope_t(scope_t& _parent, scope_t& _grandchild) : child_scope_t(_parent), grandchild(_grandchild) { DEBUG("scope.symbols", "Binding scope " << &_parent << " with " << &_grandchild); TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&"); } virtual ~bind_scope_t() { TRACE_DTOR(bind_scope_t); } virtual string description() { return grandchild.description(); } virtual void define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) { parent->define(kind, name, def); grandchild.define(kind, name, def); } virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name) { if (expr_t::ptr_op_t def = grandchild.lookup(kind, name)) return def; return child_scope_t::lookup(kind, name); } }; template <typename T> T * search_scope(scope_t * ptr, bool prefer_direct_parents = false) { DEBUG("scope.search", "Searching scope " << ptr->description()); if (T * sought = dynamic_cast<T *>(ptr)) return sought; if (bind_scope_t * scope = dynamic_cast<bind_scope_t *>(ptr)) { if (T * sought = search_scope<T>(prefer_direct_parents ? scope->parent : &scope->grandchild)) return sought; return search_scope<T>(prefer_direct_parents ? &scope->grandchild : scope->parent); } else if (child_scope_t * child_scope = dynamic_cast<child_scope_t *>(ptr)) { return search_scope<T>(child_scope->parent); } return NULL; } template <typename T> inline T& find_scope(child_scope_t& scope, bool skip_this = true, bool prefer_direct_parents = false) { if (T * sought = search_scope<T>(skip_this ? scope.parent : &scope, prefer_direct_parents)) return *sought; throw_(std::runtime_error, _("Could not find scope")); return reinterpret_cast<T&>(scope); // never executed } template <typename T> inline T& find_scope(scope_t& scope, bool prefer_direct_parents = false) { if (T * sought = search_scope<T>(&scope, prefer_direct_parents)) return *sought; throw_(std::runtime_error, _("Could not find scope")); return reinterpret_cast<T&>(scope); // never executed } class symbol_scope_t : public child_scope_t { typedef std::map<symbol_t, expr_t::ptr_op_t> symbol_map; optional<symbol_map> symbols; public: explicit symbol_scope_t() : child_scope_t() { TRACE_CTOR(symbol_scope_t, ""); } explicit symbol_scope_t(scope_t& _parent) : child_scope_t(_parent) { TRACE_CTOR(symbol_scope_t, "scope_t&"); } virtual ~symbol_scope_t() { TRACE_DTOR(symbol_scope_t); } virtual string description() { if (parent) return parent->description(); #if !NO_ASSERTS else assert(false); #endif return empty_string; } virtual void define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); }; class context_scope_t : public child_scope_t { value_t::type_t value_type_context; bool required; public: explicit context_scope_t(scope_t& _parent, value_t::type_t _type_context = value_t::VOID, const bool _required = true) : child_scope_t(_parent), value_type_context(_type_context), required(_required) { TRACE_CTOR(context_scope_t, "scope_t&, value_t::type_t, bool"); } virtual ~context_scope_t() { TRACE_DTOR(context_scope_t); } virtual string description() { return parent->description(); } virtual value_t::type_t type_context() const { return value_type_context; } virtual bool type_required() const { return required; } }; class call_scope_t : public context_scope_t { public: value_t args; mutable void * ptr; value_t& resolve(const std::size_t index, value_t::type_t context = value_t::VOID, const bool required = false); public: expr_t::ptr_op_t * locus; const int depth; explicit call_scope_t(scope_t& _parent, expr_t::ptr_op_t * _locus = NULL, const int _depth = 0) : context_scope_t(_parent, _parent.type_context(), _parent.type_required()), ptr(NULL), locus(_locus), depth(_depth) { TRACE_CTOR(call_scope_t, "scope_t&, expr_t::ptr_op_t *, const int"); } virtual ~call_scope_t() { TRACE_DTOR(call_scope_t); } void set_args(const value_t& _args) { args = _args; } value_t& value() { // Make sure that all of the arguments have been resolved. for (std::size_t index = 0; index < args.size(); index++) resolve(index); return args; } value_t& operator[](const std::size_t index) { return resolve(index); } #if 0 const value_t& operator[](const std::size_t index) const { return args[index]; } #endif bool has(std::size_t index) { return index < args.size() && ! (*this)[index].is_null(); } template <typename T> bool has(std::size_t index); template <typename T> T get(std::size_t index, bool convert = true); template <typename T> T& context() { if (ptr == NULL) ptr = &find_scope<T>(*this); assert(ptr != NULL); return *static_cast<T *>(ptr); } void push_front(const value_t& val) { args.push_front(val); } void push_back(const value_t& val) { args.push_back(val); } void pop_back() { args.pop_back(); } typedef value_t::sequence_t::iterator iterator; value_t::sequence_t::iterator begin() { return args.begin(); } value_t::sequence_t::iterator end() { return args.end(); } typedef value_t::sequence_t::const_iterator const_iterator; value_t::sequence_t::const_iterator begin() const { return args.begin(); } value_t::sequence_t::const_iterator end() const { return args.end(); } std::size_t size() const { return args.size(); } bool empty() const { return args.size() == 0; } }; template <> inline bool call_scope_t::has<bool>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::BOOLEAN, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::has<int>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::INTEGER, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::has<long>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::INTEGER, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::has<amount_t>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::AMOUNT, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::has<balance_t>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::BALANCE, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::has<string>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::STRING, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::has<date_t>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::DATE, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::has<datetime_t>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::DATETIME, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::has<scope_t *>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::SCOPE, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::has<expr_t::ptr_op_t>(std::size_t index) { if (index < args.size()) { resolve(index, value_t::ANY, false); return ! args[index].is_null(); } return false; } template <> inline bool call_scope_t::get<bool>(std::size_t index, bool convert) { if (convert) return resolve(index, value_t::BOOLEAN, false).to_boolean(); else return resolve(index, value_t::BOOLEAN).as_boolean(); } template <> inline int call_scope_t::get<int>(std::size_t index, bool) { return resolve(index, value_t::INTEGER, false).to_int(); } template <> inline long call_scope_t::get<long>(std::size_t index, bool convert) { if (convert) return resolve(index, value_t::INTEGER, false).to_long(); else return resolve(index, value_t::INTEGER).as_long(); } template <> inline amount_t call_scope_t::get<amount_t>(std::size_t index, bool convert) { if (convert) return resolve(index, value_t::AMOUNT, false).to_amount(); else return resolve(index, value_t::AMOUNT).as_amount(); } template <> inline balance_t call_scope_t::get<balance_t>(std::size_t index, bool convert) { if (convert) return resolve(index, value_t::BALANCE, false).to_balance(); else return resolve(index, value_t::BALANCE).as_balance(); } template <> inline string call_scope_t::get<string>(std::size_t index, bool convert) { if (convert) return resolve(index, value_t::STRING, false).to_string(); else return resolve(index, value_t::STRING).as_string(); } template <> inline mask_t call_scope_t::get<mask_t>(std::size_t index, bool convert) { if (convert) return resolve(index, value_t::MASK, false).to_mask(); else return resolve(index, value_t::MASK).as_mask(); } template <> inline date_t call_scope_t::get<date_t>(std::size_t index, bool convert) { if (convert) return resolve(index, value_t::DATE, false).to_date(); else return resolve(index, value_t::DATE).as_date(); } template <> inline datetime_t call_scope_t::get<datetime_t>(std::size_t index, bool convert) { if (convert) return resolve(index, value_t::DATETIME, false).to_datetime(); else return resolve(index, value_t::DATETIME).as_datetime(); } #if 0 template <> inline value_t::sequence_t& call_scope_t::get<value_t::sequence_t&>(std::size_t index, bool) { return resolve(index, value_t::SEQUENCE).as_sequence_lval(); } template <> inline const value_t::sequence_t& call_scope_t::get<const value_t::sequence_t&>(std::size_t index, bool) { return resolve(index, value_t::SEQUENCE).as_sequence(); } #endif template <> inline scope_t * call_scope_t::get<scope_t *>(std::size_t index, bool) { return resolve(index, value_t::SCOPE).as_scope(); } template <> inline expr_t::ptr_op_t call_scope_t::get<expr_t::ptr_op_t>(std::size_t index, bool) { return args[index].as_any<expr_t::ptr_op_t>(); } inline string join_args(call_scope_t& args) { std::ostringstream buf; bool first = true; for (std::size_t i = 0; i < args.size(); i++) { if (first) first = false; else buf << ' '; buf << args[i]; } return buf.str(); } class value_scope_t : public child_scope_t { value_t value; value_t get_value(call_scope_t&) { return value; } public: value_scope_t(scope_t& _parent, const value_t& _value) : child_scope_t(_parent), value(_value) { TRACE_CTOR(value_scope_t, "scope_t&, value_t"); } ~value_scope_t() throw() { TRACE_DTOR(value_scope_t); } virtual string description() { return parent->description(); } virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name) { if (kind != symbol_t::FUNCTION) return NULL; if (name == "value") return MAKE_FUNCTOR(value_scope_t::get_value); return child_scope_t::lookup(kind, name); } }; } // namespace ledger #endif // INCLUDED_SCOPE_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/select.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000034366�14411236400�0015345�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "select.h" #include "journal.h" #include "account.h" #include "report.h" #include "output.h" #include "print.h" #include "chain.h" #include "filters.h" #include "scope.h" #include "op.h" namespace ledger { namespace { bool get_principal_identifiers(expr_t::ptr_op_t expr, string& ident, bool do_transforms = false) { bool result = true; if (expr->is_ident()) { string name(expr->as_ident()); if (name == "date" || name == "aux_date" || name == "payee") { if (! ident.empty() && ! (name == "date" || name == "aux_date" || name == "payee")) result = false; ident = name; } else if (name == "account") { if (! ident.empty() && ! (name == "account")) result = false; ident = name; if (do_transforms) expr->set_ident("display_account"); } else if (name == "amount") { if (! ident.empty() && ! (name == "amount")) result = false; ident = name; if (do_transforms) expr->set_ident("display_amount"); } else if (name == "total") { if (! ident.empty() && ! (name == "total")) result = false; ident = name; if (do_transforms) expr->set_ident("display_total"); } } if (expr->kind > expr_t::op_t::TERMINALS || expr->is_scope()) { if (expr->left()) { if (! get_principal_identifiers(expr->left(), ident, do_transforms)) result = false; if (expr->kind > expr_t::op_t::UNARY_OPERATORS && expr->has_right()) if (! get_principal_identifiers(expr->right(), ident, do_transforms)) result = false; } } return result; } } value_t select_command(call_scope_t& args) { string text = "select " + join_args(args); if (text.empty()) throw std::logic_error(_("Usage: select TEXT")); report_t& report(find_scope<report_t>(args)); // Our first step is to divide the select statement into its principal // parts: // // SELECT <VALEXPR-LIST> // FROM <NAME> // WHERE <VALEXPR> // DISPLAY <VALEXPR> // COLLECT <VALEXPR> // GROUP BY <VALEXPR> // STYLE <NAME> boost::regex select_re ("(select|from|where|display|collect|group\\s+by|style)\\s+" "(.+?)" "(?=(\\s+(from|where|display|collect|group\\s+by|style)\\s+|$))", boost::regex::perl | boost::regex::icase); boost::regex from_accounts_re("from\\s+accounts\\>"); bool accounts_report = boost::regex_search(text, from_accounts_re); boost::sregex_iterator m1(text.begin(), text.end(), select_re); boost::sregex_iterator m2; expr_t::ptr_op_t report_functor; std::ostringstream formatter; while (m1 != m2) { const boost::match_results<string::const_iterator>& match(*m1); string keyword(match[1]); string arg(match[2]); DEBUG("select.parse", "keyword: " << keyword); DEBUG("select.parse", "arg: " << arg); if (keyword == "select") { expr_t args_expr(arg); value_t columns(split_cons_expr(args_expr.get_op())); bool first = true; string thus_far = ""; std::size_t cols = 0; #ifdef HAVE_IOCTL struct winsize ws; #endif if (report.HANDLED(columns_)) cols = lexical_cast<std::size_t>(report.HANDLER(columns_).value); else if (const char * columns_env = std::getenv("COLUMNS")) cols = lexical_cast<std::size_t>(columns_env); #ifdef HAVE_IOCTL else if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1) cols = ws.ws_col; #endif else cols = 80; std::size_t date_width = (report.HANDLED(date_width_) ? lexical_cast<std::size_t>(report.HANDLER(date_width_).str()) : static_cast<std::size_t> (format_date(CURRENT_DATE(),FMT_PRINTED).length())); std::size_t payee_width = (report.HANDLED(payee_width_) ? lexical_cast<std::size_t>(report.HANDLER(payee_width_).str()) : std::size_t(double(cols) * 0.263157)); std::size_t account_width = (report.HANDLED(account_width_) ? lexical_cast<std::size_t>(report.HANDLER(account_width_).str()) : std::size_t(double(cols) * 0.302631)); std::size_t amount_width = (report.HANDLED(amount_width_) ? lexical_cast<std::size_t>(report.HANDLER(amount_width_).str()) : std::size_t(double(cols) * 0.157894)); std::size_t total_width = (report.HANDLED(total_width_) ? lexical_cast<std::size_t>(report.HANDLER(total_width_).str()) : amount_width); std::size_t meta_width = (report.HANDLED(meta_width_) ? lexical_cast<std::size_t>(report.HANDLER(meta_width_).str()) : 10); bool saw_payee = false; bool saw_account = false; std::size_t cols_needed = 0; foreach (const value_t& column, columns.to_sequence()) { string ident; if (get_principal_identifiers(as_expr(column), ident)) { if (ident == "date" || ident == "aux_date") { cols_needed += date_width + 1; } else if (ident == "payee") { cols_needed += payee_width + 1; saw_payee = true; } else if (ident == "account") { cols_needed += account_width + 1; saw_account = true; } else if (ident == "amount") { cols_needed += amount_width + 1; } else if (ident == "total") { cols_needed += total_width + 1; } else { cols_needed += meta_width + 1; } } } while ((saw_account || saw_payee) && cols_needed < cols) { if (saw_account && cols_needed < cols) { ++account_width; ++cols_needed; if (cols_needed < cols) { ++account_width; ++cols_needed; } } if (saw_payee && cols_needed < cols) { ++payee_width; ++cols_needed; } } while ((saw_account || saw_payee) && cols_needed > cols && account_width > 5 && payee_width > 5) { DEBUG("auto.columns", "adjusting account down"); if (saw_account && cols_needed > cols) { --account_width; --cols_needed; if (cols_needed > cols) { --account_width; --cols_needed; } } if (saw_payee && cols_needed > cols) { --payee_width; --cols_needed; } DEBUG("auto.columns", "account_width now = " << account_width); } if (! report.HANDLED(date_width_)) report.HANDLER(date_width_).value = to_string(date_width); if (! report.HANDLED(payee_width_)) report.HANDLER(payee_width_).value = to_string(payee_width); if (! report.HANDLED(account_width_)) report.HANDLER(account_width_).value = to_string(account_width); if (! report.HANDLED(amount_width_)) report.HANDLER(amount_width_).value = to_string(amount_width); if (! report.HANDLED(total_width_)) report.HANDLER(total_width_).value = to_string(total_width); foreach (const value_t& column, columns.to_sequence()) { if (first) first = false; else formatter << ' '; formatter << "%("; string ident; if (get_principal_identifiers(as_expr(column), ident, true)) { if (ident == "date" || ident == "aux_date") { formatter << "ansify_if(" << "ansify_if(justify(format_date("; as_expr(column)->print(formatter); formatter << "), int(date_width)),"; formatter << "green if color and date > today)," << "bold if should_bold)"; if (! thus_far.empty()) thus_far += " + "; thus_far += "int(date_width) + 1"; } else if (ident == "payee") { formatter << "ansify_if(" << "ansify_if(justify(truncated("; as_expr(column)->print(formatter); formatter << ", int(payee_width)), int(payee_width)),"; formatter << "bold if color and !cleared and actual)," << "bold if should_bold)"; if (! thus_far.empty()) thus_far += " + "; thus_far += "int(payee_width) + 1"; } else if (ident == "account") { formatter << "ansify_if("; if (accounts_report) { formatter << "ansify_if("; formatter << "partial_account(options.flat), blue if color),"; } else { formatter << "justify(truncated("; as_expr(column)->print(formatter); formatter << ", int(account_width), int(abbrev_len))," << "int(account_width), -1, "; formatter << "false, color),"; if (! thus_far.empty()) thus_far += " + "; thus_far += "int(account_width) + 1"; } formatter << " bold if should_bold)"; } else if (ident == "amount" || ident == "total") { formatter << "ansify_if(" << "justify(scrub("; as_expr(column)->print(formatter); formatter << "), "; if (ident == "amount") formatter << "int(amount_width),"; else formatter << "int(total_width),"; if (! thus_far.empty()) thus_far += " + "; if (ident == "amount") thus_far += "int(amount_width)"; else thus_far += "int(total_width)"; if (thus_far.empty()) formatter << "-1"; else formatter << thus_far; formatter << ", true, color)," << " bold if should_bold)"; thus_far += " + 1"; } else { formatter << "ansify_if(" << "justify(truncated("; as_expr(column)->print(formatter); formatter << ", int(meta_width or 10)), int(meta_width) or 10),"; formatter << "bold if should_bold)"; if (! thus_far.empty()) thus_far += " + "; thus_far += "(int(meta_width) or 10) + 1"; } } formatter << ")"; } formatter << "\\n"; DEBUG("select.parse", "formatter: " << formatter.str()); } else if (keyword == "from") { if (arg == "xacts" || arg == "txns" || arg == "transactions") { report_functor = expr_t::op_t::wrap_functor (reporter<>(post_handler_ptr(new print_xacts(report, report.HANDLED(raw))), report, string("#select"))); } else if (arg == "posts" || arg == "postings") { report_functor = expr_t::op_t::wrap_functor (reporter<>(post_handler_ptr(new format_posts(report, formatter.str())), report, string("#select"))); } else if (arg == "accounts") { report_functor = expr_t::op_t::wrap_functor (reporter<account_t, acct_handler_ptr, &report_t::accounts_report> (acct_handler_ptr(new format_accounts(report, formatter.str())), report, string("#select"))); } else if (arg == "commodities") { report_functor = expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::commodities_report> (post_handler_ptr(new format_posts(report, formatter.str())), report, string("#select"))); } } else if (keyword == "where") { #if 0 query_t query; keep_details_t keeper(true, true, true); expr_t::ptr_op_t expr = query.parse_args(string_value(arg).to_sequence(), keeper, false, true); report.HANDLER(limit_).on("#select", query.get_query(query_t::QUERY_LIMIT)); #else report.HANDLER(limit_).on("#select", arg); #endif } else if (keyword == "display") { report.HANDLER(display_).on("#select", arg); } else if (keyword == "collect") { report.HANDLER(amount_).on("#select", arg); } else if (keyword == "group by") { report.HANDLER(group_by_).on("#select", arg); } else if (keyword == "style") { if (arg == "csv") { } else if (arg == "xml") { } else if (arg == "json") { } else if (arg == "emacs") { } } ++m1; } if (! report_functor) { report_functor = expr_t::op_t::wrap_functor (reporter<>(post_handler_ptr(new format_posts(report, formatter.str())), report, string("#select"))); } call_scope_t call_args(report); return report_functor->as_function()(call_args); } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/select.h���������������������������������������������������������������������������0000664�0000000�0000000�00000003570�14411236400�0015200�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup select */ /** * @file select.h * @author John Wiegley * * @ingroup select */ #ifndef INCLUDED_SELECT_H #define INCLUDED_SELECT_H #include "utils.h" #include "value.h" namespace ledger { class call_scope_t; value_t select_command(call_scope_t& args); } // namespace ledger #endif // INCLUDED_SELECT_H ����������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/session.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000024712�14411236400�0015543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "session.h" #include "xact.h" #include "account.h" #include "journal.h" #include "iterators.h" #include "filters.h" namespace ledger { void set_session_context(session_t * session) { if (session) { times_initialize(); amount_t::initialize(); amount_t::parse_conversion("1.0m", "60s"); amount_t::parse_conversion("1.00h", "60m"); value_t::initialize(); } else if (! session) { value_t::shutdown(); amount_t::shutdown(); times_shutdown(); } } session_t::session_t() : flush_on_next_data_file(false), journal(new journal_t) { parsing_context.push(); TRACE_CTOR(session_t, ""); } std::size_t session_t::read_data(const string& master_account) { bool populated_data_files = false; if (HANDLER(file_).data_files.empty()) { path file; if (const char * home_var = std::getenv("HOME")) file = path(home_var) / ".ledger"; if (! file.empty() && exists(file)) HANDLER(file_).data_files.push_back(file); else throw_(parse_error, "No journal file was specified (please use -f)"); populated_data_files = true; } std::size_t xact_count = 0; account_t * acct; if (master_account.empty()) acct = journal->master; else acct = journal->find_account(master_account); optional<path> price_db_path; if (HANDLED(price_db_)) { price_db_path = resolve_path(HANDLER(price_db_).str()); if (!exists(price_db_path.get())) { throw_(parse_error, _f("Could not find specified price-db file %1%") % price_db_path); } } else { if (const char * home_var = std::getenv("HOME")) { price_db_path = (path(home_var) / ".pricedb"); } else { price_db_path = ("./.ledgerrc"); } } if (HANDLED(day_break)) journal->day_break = true; if (HANDLED(recursive_aliases)) journal->recursive_aliases = true; if (HANDLED(no_aliases)) journal->no_aliases = true; if (HANDLED(explicit)) { // No-op } if (HANDLED(check_payees)) journal->check_payees = true; if (HANDLED(permissive)) journal->checking_style = journal_t::CHECK_PERMISSIVE; else if (HANDLED(pedantic)) journal->checking_style = journal_t::CHECK_ERROR; else if (HANDLED(strict)) journal->checking_style = journal_t::CHECK_WARNING; if (HANDLED(value_expr_)) journal->value_expr = HANDLER(value_expr_).str(); if (price_db_path) { if (exists(*price_db_path)) { parsing_context.push(*price_db_path); parsing_context.get_current().journal = journal.get(); try { if (journal->read(parsing_context) > 0) throw_(parse_error, _("Transactions not allowed in price history file")); } catch (...) { parsing_context.pop(); throw; } parsing_context.pop(); } } foreach (const path& pathname, HANDLER(file_).data_files) { if (pathname == "-" || pathname == "/dev/stdin") { // To avoid problems with stdin and pipes, etc., we read the entire // file in beforehand into a memory buffer, and then parcel it out // from there. std::ostringstream buffer; while (std::cin.good() && ! std::cin.eof()) { char line[8192]; std::cin.read(line, 8192); std::streamsize count = std::cin.gcount(); buffer.write(line, count); } buffer.flush(); shared_ptr<std::istream> stream(new std::istringstream(buffer.str())); parsing_context.push(stream); } else { parsing_context.push(pathname); } parsing_context.get_current().journal = journal.get(); parsing_context.get_current().master = acct; try { xact_count += journal->read(parsing_context); } catch (...) { parsing_context.pop(); throw; } parsing_context.pop(); } DEBUG("ledger.read", "xact_count [" << xact_count << "] == journal->xacts.size() [" << journal->xacts.size() << "]"); assert(xact_count == journal->xacts.size()); if (populated_data_files) HANDLER(file_).data_files.clear(); VERIFY(journal->valid()); return journal->xacts.size(); } journal_t * session_t::read_journal_files() { INFO_START(journal, "Read journal file"); string master_account; if (HANDLED(master_account_)) master_account = HANDLER(master_account_).str(); #if DEBUG_ON std::size_t count = #endif read_data(master_account); INFO_FINISH(journal); #if DEBUG_ON INFO("Found " << count << " transactions"); #endif return journal.get(); } journal_t * session_t::read_journal(const path& pathname) { HANDLER(file_).data_files.clear(); HANDLER(file_).data_files.push_back(pathname); return read_journal_files(); } journal_t * session_t::read_journal_from_string(const string& data) { HANDLER(file_).data_files.clear(); shared_ptr<std::istream> stream(new std::istringstream(data)); parsing_context.push(stream); parsing_context.get_current().journal = journal.get(); parsing_context.get_current().master = journal->master; try { journal->read(parsing_context); } catch (...) { parsing_context.pop(); throw; } parsing_context.pop(); return journal.get(); } void session_t::close_journal_files() { journal.reset(); amount_t::shutdown(); journal.reset(new journal_t); amount_t::initialize(); } journal_t * session_t::get_journal() { return journal.get(); } value_t session_t::fn_account(call_scope_t& args) { if (args[0].is_string()) return scope_value(journal->find_account(args.get<string>(0), false)); else if (args[0].is_mask()) return scope_value(journal->find_account_re(args.get<mask_t>(0).str())); else return NULL_VALUE; } value_t session_t::fn_min(call_scope_t& args) { return args[1] < args[0] ? args[1] : args[0]; } value_t session_t::fn_max(call_scope_t& args) { return args[1] > args[0] ? args[1] : args[0]; } value_t session_t::fn_int(call_scope_t& args) { return args[0].to_long(); } value_t session_t::fn_str(call_scope_t& args) { return string_value(args[0].to_string()); } value_t session_t::fn_lot_price(call_scope_t& args) { amount_t amt(args.get<amount_t>(0, false)); if (amt.has_annotation() && amt.annotation().price) return *amt.annotation().price; else return NULL_VALUE; } value_t session_t::fn_lot_date(call_scope_t& args) { amount_t amt(args.get<amount_t>(0, false)); if (amt.has_annotation() && amt.annotation().date) return *amt.annotation().date; else return NULL_VALUE; } value_t session_t::fn_lot_tag(call_scope_t& args) { amount_t amt(args.get<amount_t>(0, false)); if (amt.has_annotation() && amt.annotation().tag) return string_value(*amt.annotation().tag); else return NULL_VALUE; } option_t<session_t> * session_t::lookup_option(const char * p) { switch (*p) { case 'Q': OPT_CH(download); // -Q break; case 'Z': OPT_CH(price_exp_); break; case 'c': OPT(check_payees); break; case 'd': OPT(download); // -Q else OPT(decimal_comma); else OPT(day_break); break; case 'e': OPT(explicit); break; case 'f': OPT_(file_); // -f break; case 'i': OPT(input_date_format_); break; case 'l': OPT_ALT(price_exp_, leeway_); break; case 'm': OPT(master_account_); break; case 'n': OPT(no_aliases); break; case 'p': OPT(price_db_); else OPT(price_exp_); else OPT(pedantic); else OPT(permissive); break; case 'r': OPT(recursive_aliases); break; case 's': OPT(strict); break; case 't': OPT(time_colon); break; case 'v': OPT(value_expr_); } return NULL; } expr_t::ptr_op_t session_t::lookup(const symbol_t::kind_t kind, const string& name) { const char * p = name.c_str(); switch (kind) { case symbol_t::FUNCTION: switch (*p) { case 'a': if (is_eq(p, "account")) return MAKE_FUNCTOR(session_t::fn_account); break; case 'l': if (is_eq(p, "lot_price")) return MAKE_FUNCTOR(session_t::fn_lot_price); else if (is_eq(p, "lot_date")) return MAKE_FUNCTOR(session_t::fn_lot_date); else if (is_eq(p, "lot_tag")) return MAKE_FUNCTOR(session_t::fn_lot_tag); break; case 'i': if (is_eq(p, "int")) return MAKE_FUNCTOR(session_t::fn_int); break; case 'm': if (is_eq(p, "min")) return MAKE_FUNCTOR(session_t::fn_min); else if (is_eq(p, "max")) return MAKE_FUNCTOR(session_t::fn_max); break; case 's': if (is_eq(p, "str")) return MAKE_FUNCTOR(session_t::fn_str); break; default: break; } // Check if they are trying to access an option's setting or value. if (option_t<session_t> * handler = lookup_option(p)) return MAKE_OPT_FUNCTOR(session_t, handler); break; case symbol_t::OPTION: if (option_t<session_t> * handler = lookup_option(p)) return MAKE_OPT_HANDLER(session_t, handler); break; default: break; } return symbol_scope_t::lookup(kind, name); } } // namespace ledger ������������������������������������������������������ledger-3.3.2/src/session.h��������������������������������������������������������������������������0000664�0000000�0000000�00000013257�14411236400�0015407�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @defgroup report Reporting */ /** * @file session.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_SESSION_H #define INCLUDED_SESSION_H #include "account.h" #include "journal.h" #include "context.h" #include "option.h" #include "commodity.h" namespace ledger { class xact_t; struct ComparePaths { bool operator()(const path& p1, const path& p2) const { return p1 < p2 && !boost::filesystem::equivalent(p1, p2); } }; #define COMMA , class session_t : public symbol_scope_t { friend void set_session_context(session_t * session); public: bool flush_on_next_data_file; unique_ptr<journal_t> journal; parse_context_stack_t parsing_context; optional<expr_t> value_expr; explicit session_t(); virtual ~session_t() { TRACE_DTOR(session_t); parsing_context.pop(); } virtual string description() { return _("current session"); } void set_flush_on_next_data_file(const bool truth) { flush_on_next_data_file = truth; } journal_t * read_journal(const path& pathname); journal_t * read_journal_from_string(const string& data); std::size_t read_data(const string& master_account = ""); journal_t * read_journal_files(); void close_journal_files(); journal_t * get_journal(); value_t fn_account(call_scope_t& scope); value_t fn_min(call_scope_t& scope); value_t fn_max(call_scope_t& scope); value_t fn_int(call_scope_t& scope); value_t fn_str(call_scope_t& scope); value_t fn_lot_price(call_scope_t& scope); value_t fn_lot_date(call_scope_t& scope); value_t fn_lot_tag(call_scope_t& scope); void report_options(std::ostream& out) { HANDLER(check_payees).report(out); HANDLER(day_break).report(out); HANDLER(download).report(out); HANDLER(decimal_comma).report(out); HANDLER(time_colon).report(out); HANDLER(file_).report(out); HANDLER(input_date_format_).report(out); HANDLER(explicit).report(out); HANDLER(master_account_).report(out); HANDLER(pedantic).report(out); HANDLER(permissive).report(out); HANDLER(price_db_).report(out); HANDLER(price_exp_).report(out); HANDLER(recursive_aliases).report(out); HANDLER(no_aliases).report(out); HANDLER(strict).report(out); HANDLER(value_expr_).report(out); } option_t<session_t> * lookup_option(const char * p); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); /** * Option handlers */ OPTION(session_t, check_payees); OPTION(session_t, day_break); OPTION(session_t, download); // -Q OPTION_(session_t, decimal_comma, DO() { commodity_t::decimal_comma_by_default = true; }); OPTION_(session_t, time_colon, DO() { commodity_t::time_colon_by_default = true; }); OPTION__ (session_t, price_exp_, // -Z CTOR(session_t, price_exp_) { value = "24"; }); OPTION__ (session_t, file_, // -f std::list<path> data_files; CTOR(session_t, file_) {} DO_(str) { if (parent->flush_on_next_data_file) { data_files.clear(); parent->flush_on_next_data_file = false; } data_files.push_back(str); }); OPTION_(session_t, input_date_format_, DO_(str) { // This changes static variables inside times.h, which affects the // basic date parser. set_input_date_format(str.c_str()); }); OPTION(session_t, explicit); OPTION(session_t, master_account_); OPTION(session_t, pedantic); OPTION(session_t, permissive); OPTION(session_t, price_db_); OPTION(session_t, strict); OPTION(session_t, value_expr_); OPTION(session_t, recursive_aliases); OPTION(session_t, no_aliases); }; /** * Set the current session context, transferring all static globals to point * at the data structures related to this session. Although Ledger itself is * not thread-safe, by locking, switching session context, then unlocking * after an operation is done, multiple threads can sequentially make use of * the library. Thus, a session_t maintains all of the information relating * to a single usage of the Ledger library. */ void set_session_context(session_t * session); } // namespace ledger #endif // INCLUDED_SESSION_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/stats.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000007614�14411236400�0015220�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "draft.h" #include "xact.h" #include "post.h" #include "account.h" #include "report.h" #include "session.h" namespace ledger { value_t report_statistics(call_scope_t& args) { report_t& report(find_scope<report_t>(args)); std::ostream& out(report.output_stream); const account_t::xdata_t::details_t& statistics(report.session.journal->master->family_details(true)); if (! is_valid(statistics.earliest_post) && ! is_valid(statistics.latest_post)) return NULL_VALUE; assert(is_valid(statistics.earliest_post)); assert(is_valid(statistics.latest_post)); out << format(_f("Time period: %1% to %2% (%3% days)") % format_date(statistics.earliest_post) % format_date(statistics.latest_post) % (statistics.latest_post - statistics.earliest_post).days()) << std::endl << std::endl; out << _(" Files these postings came from:") << std::endl; foreach (const path& pathname, statistics.filenames) if (! pathname.empty()) out << " " << pathname.string() << std::endl; out << std::endl; out << _(" Unique payees: "); out.width(6); out << statistics.payees_referenced.size() << std::endl; out << _(" Unique accounts: "); out.width(6); out << statistics.accounts_referenced.size() << std::endl; out << std::endl; out << _(" Number of postings: "); out.width(6); out << statistics.posts_count; out << " ("; out.precision(2); out << (double(statistics.posts_count)/ double((statistics.latest_post - statistics.earliest_post).days())) << _(" per day)") << std::endl; out << _(" Uncleared postings: "); out.width(6); out << (statistics.posts_count - statistics.posts_cleared_count) << std::endl; out << std::endl; out << _(" Days since last post: "); out.width(6); out << (CURRENT_DATE() - statistics.latest_post).days() << std::endl; out << _(" Posts in last 7 days: "); out.width(6); out << statistics.posts_last_7_count << std::endl; out << _(" Posts in last 30 days: "); out.width(6); out << statistics.posts_last_30_count << std::endl; out << _(" Posts seen this month: "); out.width(6); out << statistics.posts_this_month_count << std::endl; out.flush(); return NULL_VALUE; } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/stats.h����������������������������������������������������������������������������0000664�0000000�0000000�00000003545�14411236400�0015061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup stats */ /** * @file stats.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_STATS_H #define INCLUDED_STATS_H #include "value.h" namespace ledger { class call_scope_t; value_t report_statistics(call_scope_t& scope); } // namespace ledger #endif // INCLUDED_STATS_H �����������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/stream.cc��������������������������������������������������������������������������0000664�0000000�0000000�00000010411�14411236400�0015342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "stream.h" namespace ledger { namespace { /** * @brief Forks a child process so that Ledger may handle running a * pager * * In order for the pager option to work, Ledger has to run the pager * itself, which requires Ledger to fork a new process in order to run * the pager. This function does the necessary fork. After the fork, * two processes exist. One of them is exec'd to create the pager; * the other is still Ledger. * * This function returns only for the process that is still Ledger. * * @param pager_path Path to the pager command. * * @return The file descriptor of the pipe to the pager. The caller * is responsible for cleaning this up. * * @exception std::logic_error Some problem was encountered, such as * failure to create a pipe or failure to fork a child process. */ int do_fork(std::ostream ** os, const path& pager_path) { #if !defined(_WIN32) && !defined(__CYGWIN__) int pfd[2]; int status = pipe(pfd); if (status == -1) throw std::logic_error(_("Failed to create pipe")); status = fork(); if (status < 0) { throw std::logic_error(_("Failed to fork child process")); } else if (status == 0) { // child // Duplicate pipe's reading end into stdin status = dup2(pfd[0], STDIN_FILENO); if (status == -1) perror("dup2"); // Close unused file descriptors: the pipe's writing and reading // ends (the latter is not needed anymore, after the duplication). close(pfd[1]); close(pfd[0]); execlp("/bin/sh", "/bin/sh", "-c", pager_path.string().c_str(), NULL); // We should never, ever reach here perror("execlp: /bin/sh"); exit(1); } else { // parent close(pfd[0]); typedef iostreams::stream<iostreams::file_descriptor_sink> fdstream; *os = new fdstream(pfd[1], iostreams::never_close_handle); } return pfd[1]; #else return 0; #endif } } void output_stream_t::initialize(const optional<path>& output_file, const optional<path>& pager_path) { if (output_file && *output_file != "-") os = new ofstream(*output_file); else if (pager_path) pipe_to_pager_fd = do_fork(&os, *pager_path); else os = &std::cout; } void output_stream_t::close() { #if !defined(_WIN32) && !defined(__CYGWIN__) if (os != &std::cout) { checked_delete(os); os = &std::cout; } if (pipe_to_pager_fd != -1) { ::close(pipe_to_pager_fd); pipe_to_pager_fd = -1; int status; wait(&status); if (! WIFEXITED(status) || WEXITSTATUS(status) != 0) throw std::logic_error(_("Error in the pager")); } #endif } } // namespace ledger �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/stream.h���������������������������������������������������������������������������0000664�0000000�0000000�00000010366�14411236400�0015215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup util */ /** * @file stream.h * @author John Wiegley, Omari Norman * * @ingroup util * * @brief A utility class for abstracting an output stream. * * Because Ledger might send output to a file, the console, or a pager * child process, different cleanup is needed for each scenario. This * file abstracts those various needs. */ #ifndef INCLUDED_STREAM_H #define INCLUDED_STREAM_H #include "utils.h" namespace ledger { /** * @brief An output stream * * A stream to output in Ledger may be going to one of three places: * to stdout, to a file, or to a pager. Construct an output_stream_t and * the stream will automatically be cleaned up upon destruction. * * This class suffers from "else-if-heimer's disease," see Marshall * Cline's "C++ FAQ Lite". Arguably this should be three different * classes, but that introduces additional unneeded complications. */ class output_stream_t { output_stream_t& operator=(const output_stream_t&); private: int pipe_to_pager_fd; public: /** * A pointer to the ostream. Don't delete this; the output_stream_t * class takes care of this. */ std::ostream * os; /** * Construct a new output_stream_t. */ output_stream_t() : pipe_to_pager_fd(-1), os(&std::cout) { TRACE_CTOR(output_stream_t, ""); } /** * When copy-constructed, make the copy just be a new output stream. This * allows large classes to rely on their default copy-constructor without * worrying about pointer copying within output_stream_t. */ output_stream_t(const output_stream_t&) : pipe_to_pager_fd(-1), os(&std::cout) { TRACE_CTOR(output_stream_t, "copy"); } /** * Destroys an output_stream_t. This deletes the dynamically * allocated ostream, if necessary. It also closes output file * descriptor, if necessary. */ ~output_stream_t() { TRACE_DTOR(output_stream_t); close(); } /** * Initialize the output stream object. * * @param output_file File to which to send output. If both this * and pager are set, output_file takes priority. * * @param pager_path Path to a pager. To not use a pager, leave this * empty. */ void initialize(const optional<path>& output_file = none, const optional<path>& pager_path = none); /** * Converter to a standard ostream. This is used so that we can * stream directly to an object of type output_stream_t. */ operator std::ostream&() { return *os; } /** * Flushing function. A simple proxy for ostream's flush. */ void flush() { os->flush(); } /** * Close the output stream, waiting on the pager process if necessary. */ void close(); }; } // namespace ledger #endif // INCLUDED_STREAM_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/strptime.cc������������������������������������������������������������������������0000664�0000000�0000000�00000011520�14411236400�0015720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2009 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #if !(defined(_WIN32) || defined(__CYGWIN__)) #error This file should only be compiled on Windows. #endif // Implement strptime under windows #include "strptime.h" #include <time.h> #include <ctype.h> #include <string.h> #if defined(__CYGWIN__) // Define strnicmp for Cygwin. #ifndef strcmpi #define strcmpi strcasecmp #endif #ifndef stricmp #define stricmp strcasecmp #endif #ifndef strncmpi #define strncmpi strncasecmp #endif #ifndef strnicmp #define strnicmp strncasecmp #endif #endif static const char* kWeekFull[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; static const char* kWeekAbbr[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char* kMonthFull[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; static const char* kMonthAbbr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const char* _parse_num(const char* s, int low, int high, int* value) { const char* p = s; for (*value = 0; *p != NULL && isdigit(*p); ++p) { *value = (*value) * 10 + static_cast<int>(*p) - static_cast<int>('0'); } if (p == s || *value < low || *value > high) return NULL; return p; } static char* _strptime(const char *s, const char *format, struct tm *tm) { while (*format != NULL && *s != NULL) { if (*format != '%') { if (*s != *format) return NULL; ++format; ++s; continue; } ++format; int len = 0; switch (*format) { // weekday name. case 'a': case 'A': tm->tm_wday = -1; for (int i = 0; i < 7; ++i) { len = static_cast<int>(strlen(kWeekFull[i])); if (strnicmp(kWeekFull[i], s, len) == 0) { tm->tm_wday = i; break; } len = static_cast<int>(strlen(kWeekAbbr[i])); if (strnicmp(kWeekAbbr[i], s, len) == 0) { tm->tm_wday = i; break; } } if (tm->tm_wday == -1) return NULL; s += len; break; // month name. case 'b': case 'B': case 'h': tm->tm_mon = -1; for (int i = 0; i < 12; ++i) { len = static_cast<int>(strlen(kMonthFull[i])); if (strnicmp(kMonthFull[i], s, len) == 0) { tm->tm_mon = i; break; } len = static_cast<int>(strlen(kMonthAbbr[i])); if (strnicmp(kMonthAbbr[i], s, len) == 0) { tm->tm_mon = i; break; } } if (tm->tm_mon == -1) return NULL; s += len; break; // month [1, 12]. case 'm': s = _parse_num(s, 1, 12, &tm->tm_mon); if (s == NULL) return NULL; --tm->tm_mon; break; // day [1, 31]. case 'd': case 'e': s = _parse_num(s, 1, 31, &tm->tm_mday); if (s == NULL) return NULL; break; // hour [0, 23]. case 'H': s = _parse_num(s, 0, 23, &tm->tm_hour); if (s == NULL) return NULL; break; // minute [0, 59] case 'M': s = _parse_num(s, 0, 59, &tm->tm_min); if (s == NULL) return NULL; break; // seconds [0, 60]. 60 is for leap year. case 'S': s = _parse_num(s, 0, 60, &tm->tm_sec); if (s == NULL) return NULL; break; // year [1900, 9999]. case 'Y': s = _parse_num(s, 1900, 9999, &tm->tm_year); if (s == NULL) return NULL; tm->tm_year -= 1900; break; // year [0, 99]. case 'y': s = _parse_num(s, 0, 99, &tm->tm_year); if (s == NULL) return NULL; if (tm->tm_year <= 68) { tm->tm_year += 100; } break; // arbitrary whitespace. case 't': case 'n': while (isspace(*s)) ++s; break; // '%'. case '%': if (*s != '%') return NULL; ++s; break; // All the other format are not supported. default: return NULL; } ++format; } if (*format != NULL) { return NULL; } else { return const_cast<char*>(s); } } char* strptime(const char *buf, const char *fmt, struct tm *tm) { return _strptime(buf, fmt, tm); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/strptime.h�������������������������������������������������������������������������0000664�0000000�0000000�00000000231�14411236400�0015557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef INCLUDED_STRPTIME_H #define INCLUDED_STRPTIME_H char* strptime(const char *buf, const char *fmt, struct tm *tm); #endif // INCLUDED_STRPTIME_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/system.hh.in�����������������������������������������������������������������������0000664�0000000�0000000�00000015344�14411236400�0016024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup util * * @file system.hh * @author John Wiegley * * @brief All system headers needed by Ledger. * * These are collected here so that a pre-compiled header can be made. * None of these header files (with the exception of acconf.h, when * configure is re-run) are expected to change. */ #ifndef INCLUDED_SYSTEM_HH #define INCLUDED_SYSTEM_HH //#warning("Loading system.hh. This should occur only once!") /*------------------------------------------------------------------------*/ /* Application configuration */ /*------------------------------------------------------------------------*/ #define Ledger_VERSION_MAJOR @Ledger_VERSION_MAJOR@ #define Ledger_VERSION_MINOR @Ledger_VERSION_MINOR@ #define Ledger_VERSION_PATCH @Ledger_VERSION_PATCH@ #define Ledger_VERSION_PRERELEASE "@Ledger_VERSION_PRERELEASE@" #define Ledger_VERSION_DATE @Ledger_VERSION_DATE@ #define HAVE_GETTEXT @HAVE_GETTEXT@ #cmakedefine HAVE_EDIT #cmakedefine HAVE_GETPWUID #cmakedefine HAVE_GETPWNAM #cmakedefine HAVE_IOCTL #cmakedefine HAVE_ISATTY #define HAVE_UNIX_PIPES @HAVE_UNIX_PIPES@ #define HAVE_BOOST_PYTHON @HAVE_BOOST_PYTHON@ #define HAVE_GPGME @HAVE_GPGME@ #define HAVE_BOOST_REGEX_UNICODE @HAVE_BOOST_REGEX_UNICODE@ #define HAVE_BOOST_159_ISSUE_39 @HAVE_BOOST_159_ISSUE_39@ #define HAVE_BOOST_NOWIDE @HAVE_BOOST_NOWIDE@ #define DEBUG_MODE @DEBUG_MODE@ #define NO_ASSERTS @NO_ASSERTS@ #define DOCUMENT_MODEL 0 #define REDUCE_TO_INTEGER 0 /*------------------------------------------------------------------------*/ /* System includes */ /*------------------------------------------------------------------------*/ #include <algorithm> #include <exception> #include <typeinfo> #include <stdexcept> #include <iostream> #include <streambuf> #include <iomanip> #include <fstream> #include <sstream> #include <iterator> #include <list> #include <map> #include <unordered_map> #include <memory> #include <new> #include <set> #include <stack> #include <string> #include <vector> #include <cassert> #include <cctype> #include <cstdarg> #include <cstdio> #include <cstdlib> #include <cstring> #include <csignal> #if defined(_WIN32) || defined(__CYGWIN__) #include <io.h> #else #include <unistd.h> #endif #if defined(HAVE_GETPWUID) || defined(HAVE_GETPWNAM) #include <pwd.h> #endif #ifdef HAVE_IOCTL #include <sys/ioctl.h> #endif #if HAVE_UNIX_PIPES #include <sys/types.h> #include <sys/wait.h> #endif #include <cstddef> /* needed for gcc 4.9 */ #include <gmp.h> #include <mpfr.h> #include "utf8.h" #include <boost/algorithm/string.hpp> #include <boost/any.hpp> #include <boost/bind.hpp> #include <boost/cast.hpp> #include <boost/current_function.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time_io.hpp> #include <boost/date_time/gregorian/gregorian_io.hpp> #include <boost/filesystem/convenience.hpp> #include <boost/filesystem/exception.hpp> #include <boost/filesystem/fstream.hpp> #include <boost/filesystem/operations.hpp> #include <boost/filesystem/directory.hpp> #include <boost/filesystem/path.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> #include <boost/function.hpp> #include <boost/intrusive_ptr.hpp> #include <boost/iostreams/stream.hpp> #include <boost/iostreams/write.hpp> #define BOOST_IOSTREAMS_USE_DEPRECATED 1 #include <boost/iostreams/device/file_descriptor.hpp> #include <boost/iterator/iterator_facade.hpp> #include <boost/iterator/transform_iterator.hpp> #include <boost/lexical_cast.hpp> #include <boost/operators.hpp> #include <boost/optional.hpp> #include <boost/ptr_container/ptr_list.hpp> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/xml_parser.hpp> #include <boost/random/mersenne_twister.hpp> #include <boost/random/uniform_int.hpp> #include <boost/random/uniform_real.hpp> #include <boost/random/variate_generator.hpp> // jww (2012-05-20): This must be included before Boost.Regex #include <boost/xpressive/xpressive_static.hpp> #if HAVE_BOOST_REGEX_UNICODE #include <boost/regex/icu.hpp> #else #include <boost/regex.hpp> #endif // HAVE_BOOST_REGEX_UNICODE #include <boost/tokenizer.hpp> #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_comparison.hpp> #include <boost/variant.hpp> #include <boost/version.hpp> #if HAVE_GETTEXT #include <libintl.h> #define _(str) gettext(str) #else #define _(str) str #endif #define _f(str) boost::format(_(str)) #include <boost/ptr_container/ptr_deque.hpp> #if HAVE_BOOST_PYTHON #include <boost/python.hpp> #include <boost/python/detail/wrap_python.hpp> #include <boost/python/module_init.hpp> #include <boost/python/suite/indexing/vector_indexing_suite.hpp> #include <boost/iterator/indirect_iterator.hpp> #if BOOST_VERSION == 105900 && HAVE_BOOST_159_ISSUE_39 // Fix for https://github.com/boostorg/python/issues/39 namespace boost { namespace python { template <class D> inline object make_setter(D const& x) { return detail::make_setter(x, default_call_policies(), is_member_pointer<D>(), 0); } }} #endif #endif // HAVE_BOOST_PYTHON #endif // INCLUDED_SYSTEM_HH ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/temps.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000007566�14411236400�0015220�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "xact.h" #include "post.h" #include "account.h" #include "temps.h" namespace ledger { xact_t& temporaries_t::copy_xact(xact_t& origin) { if (! xact_temps) xact_temps = std::list<xact_t>(); xact_temps->push_back(origin); xact_t& temp(xact_temps->back()); temp.add_flags(ITEM_TEMP); return temp; } xact_t& temporaries_t::create_xact() { if (! xact_temps) xact_temps = std::list<xact_t>(); xact_temps->push_back(xact_t()); xact_t& temp(xact_temps->back()); temp.add_flags(ITEM_TEMP); return temp; } post_t& temporaries_t::copy_post(post_t& origin, xact_t& xact, account_t * account) { if (! post_temps) post_temps = std::list<post_t>(); post_temps->push_back(origin); post_t& temp(post_temps->back()); temp.add_flags(ITEM_TEMP); if (account) temp.account = account; temp.account->add_post(&temp); xact.add_post(&temp); return temp; } post_t& temporaries_t::create_post(xact_t& xact, account_t * account, bool bidir_link) { if (! post_temps) post_temps = std::list<post_t>(); post_temps->push_back(post_t(account)); post_t& temp(post_temps->back()); temp.add_flags(ITEM_TEMP); temp.account = account; temp.account->add_post(&temp); if (bidir_link) xact.add_post(&temp); else temp.xact = &xact; return temp; } account_t& temporaries_t::create_account(const string& name, account_t * parent) { if (! acct_temps) acct_temps = std::list<account_t>(); acct_temps->push_back(account_t(parent, name)); account_t& temp(acct_temps->back()); temp.add_flags(ACCOUNT_TEMP); if (parent) parent->add_account(&temp); return temp; } void temporaries_t::clear() { if (post_temps) { foreach (post_t& post, *post_temps) { if (! post.xact->has_flags(ITEM_TEMP)) post.xact->remove_post(&post); if (post.account && ! post.account->has_flags(ACCOUNT_TEMP)) post.account->remove_post(&post); } post_temps->clear(); } if (xact_temps) xact_temps->clear(); if (acct_temps) { foreach (account_t& acct, *acct_temps) { if (acct.parent && ! acct.parent->has_flags(ACCOUNT_TEMP)) acct.parent->remove_account(&acct); } acct_temps->clear(); } } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/temps.h����������������������������������������������������������������������������0000664�0000000�0000000�00000005224�14411236400�0015047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup report */ /** * @file temps.h * @author John Wiegley * * @ingroup report */ #ifndef INCLUDED_TEMPS_H #define INCLUDED_TEMPS_H namespace ledger { class temporaries_t { optional<std::list<xact_t> > xact_temps; optional<std::list<post_t> > post_temps; optional<std::list<account_t> > acct_temps; public: temporaries_t() { TRACE_CTOR(temporaries_t, ""); } ~temporaries_t() { TRACE_DTOR(temporaries_t); clear(); } xact_t& copy_xact(xact_t& origin); xact_t& create_xact(); xact_t& last_xact() { return xact_temps->back(); } post_t& copy_post(post_t& origin, xact_t& xact, account_t * account = NULL); post_t& create_post(xact_t& xact, account_t * account, bool bidir_link = true); post_t& last_post() { return post_temps->back(); } account_t& create_account(const string& name = "", account_t * parent = NULL); account_t& last_account() { return acct_temps->back(); } void clear(); }; } // namespace ledger #endif // INCLUDED_TEMPS_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/textual.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000165422�14411236400�0015552�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "journal.h" #include "context.h" #include "xact.h" #include "post.h" #include "account.h" #include "option.h" #include "query.h" #include "pstream.h" #include "pool.h" #include <algorithm> #if HAVE_BOOST_PYTHON #include "pyinterp.h" #endif #define TIMELOG_SUPPORT 1 #if defined(TIMELOG_SUPPORT) #include "timelog.h" #endif namespace ledger { namespace { typedef std::pair<commodity_t *, amount_t> fixed_rate_t; struct application_t { string label; variant<optional<datetime_t>, account_t *, string, fixed_rate_t> value; application_t(string _label, optional<datetime_t> epoch) : label(_label), value(epoch) {} application_t(string _label, account_t * acct) : label(_label), value(acct) {} application_t(string _label, string tag) : label(_label), value(tag) {} application_t(string _label, fixed_rate_t rate) : label(_label), value(rate) {} }; class instance_t : public noncopyable, public scope_t { public: parse_context_stack_t& context_stack; parse_context_t& context; std::istream& in; instance_t * parent; std::list<application_t> apply_stack; bool no_assertions; #if defined(TIMELOG_SUPPORT) time_log_t timelog; #endif instance_t(parse_context_stack_t& _context_stack, parse_context_t& _context, instance_t * _parent = NULL, const bool _no_assertions = false) : context_stack(_context_stack), context(_context), in(*context.stream.get()), parent(_parent), no_assertions(_no_assertions), timelog(context) {} virtual string description() { return _("textual parser"); } template <typename T> void get_applications(std::vector<T>& result) { foreach (application_t& state, apply_stack) { if (state.value.type() == typeid(T)) result.push_back(boost::get<T>(state.value)); } if (parent) parent->get_applications<T>(result); } template <typename T> optional<T> get_application() { foreach (application_t& state, apply_stack) { if (state.value.type() == typeid(T)) return boost::get<T>(state.value); } return parent ? parent->get_application<T>() : none; } account_t * top_account() { if (optional<account_t *> acct = get_application<account_t *>()) return *acct; else return NULL; } void parse(); std::streamsize read_line(char *& line); bool peek_whitespace_line() { return (in.good() && ! in.eof() && (in.peek() == ' ' || in.peek() == '\t')); } #if HAVE_BOOST_PYTHON bool peek_blank_line() { return (in.good() && ! in.eof() && (in.peek() == '\n' || in.peek() == '\r')); } #endif void read_next_directive(bool& error_flag); #if defined(TIMELOG_SUPPORT) void clock_in_directive(char * line, bool capitalized); void clock_out_directive(char * line, bool capitalized); #endif bool general_directive(char * line); void account_directive(char * line); void account_alias_directive(account_t * account, string alias); void account_payee_directive(account_t * account, string payee); void account_value_directive(account_t * account, string expr_str); void account_default_directive(account_t * account); void default_account_directive(char * args); void alias_directive(char * line); void payee_directive(char * line); void payee_alias_directive(const string& payee, string alias); void payee_uuid_directive(const string& payee, string uuid); void commodity_directive(char * line); void commodity_alias_directive(commodity_t& comm, string alias); void commodity_value_directive(commodity_t& comm, string expr_str); void commodity_format_directive(commodity_t& comm, string format); void commodity_nomarket_directive(commodity_t& comm); void commodity_default_directive(commodity_t& comm); void default_commodity_directive(char * line); void tag_directive(char * line); void apply_directive(char * line); void apply_account_directive(char * line); void apply_tag_directive(char * line); void apply_rate_directive(char * line); void apply_year_directive(char * line); void end_apply_directive(char * line); void xact_directive(char * line, std::streamsize len); void period_xact_directive(char * line); void automated_xact_directive(char * line); void price_xact_directive(char * line); void price_conversion_directive(char * line); void nomarket_directive(char * line); void include_directive(char * line); void option_directive(char * line); void comment_directive(char * line); void eval_directive(char * line); void assert_directive(char * line); void check_directive(char * line); void value_directive(char * line); void import_directive(char * line); void python_directive(char * line); post_t * parse_post(char * line, std::streamsize len, account_t * account, xact_t * xact, bool defer_expr = false); bool parse_posts(account_t * account, xact_base_t& xact, const bool defer_expr = false); xact_t * parse_xact(char * line, std::streamsize len, account_t * account); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); }; void parse_amount_expr(std::istream& in, scope_t& scope, post_t& post, amount_t& amount, const parse_flags_t& flags = PARSE_DEFAULT, const bool defer_expr = false, optional<expr_t> * amount_expr = NULL) { expr_t expr(in, flags.plus_flags(PARSE_PARTIAL)); DEBUG("textual.parse", "Parsed an amount expression"); if (expr) { if (amount_expr) *amount_expr = expr; if (! defer_expr) amount = post.resolve_expr(scope, expr); } } } void instance_t::parse() { INFO("Parsing file " << context.pathname); TRACE_START(instance_parse, 1, "Done parsing file " << context.pathname); if (! in.good() || in.eof()) return; context.linenum = 0; context.curr_pos = in.tellg(); bool error_flag = false; while (in.good() && ! in.eof()) { try { read_next_directive(error_flag); } catch (const std::exception& err) { error_flag = true; string current_context = error_context(); if (parent) { std::list<instance_t *> instances; for (instance_t * instance = parent; instance; instance = instance->parent) instances.push_front(instance); foreach (instance_t * instance, instances) add_error_context(_f("In file included from %1%") % instance->context.location()); } add_error_context(_f("While parsing file %1%") % context.location()); if (caught_signal != NONE_CAUGHT) throw; string err_context = error_context(); if (! err_context.empty()) std::cerr << err_context << std::endl; if (! current_context.empty()) std::cerr << current_context << std::endl; std::cerr << _("Error: ") << err.what() << std::endl; context.errors++; if (! current_context.empty()) context.last = current_context + "\n" + err.what(); else context.last = err.what(); } } if (apply_stack.front().value.type() == typeid(optional<datetime_t>)) epoch = boost::get<optional<datetime_t> >(apply_stack.front().value); apply_stack.pop_front(); #if defined(TIMELOG_SUPPORT) timelog.close(); #endif // TIMELOG_SUPPORT TRACE_STOP(instance_parse, 1); } std::streamsize instance_t::read_line(char *& line) { assert(in.good()); assert(! in.eof()); // no one should call us in that case context.line_beg_pos = context.curr_pos; check_for_signal(); const size_t maxLine = parse_context_t::MAX_LINE; in.getline(context.linebuf, maxLine); std::streamsize len = in.gcount(); if (in.fail() && len == (parse_context_t::MAX_LINE - 1)) { throw_(parse_error, _f("Line exceeds %1% characters") % maxLine); } if (len > 0) { context.linenum++; context.curr_pos = context.line_beg_pos; context.curr_pos += len; if (context.linenum == 0 && utf8::starts_with_bom( context.linebuf, context.linebuf + sizeof(context.linebuf))) { line = &context.linebuf[3]; len -= 3; } else { line = context.linebuf; } if (!in.eof()) { // if we are not at the end of the file, len includes the new line character, // even through it does not appear in linebuf --len; } while (len > 0 && std::isspace(line[len - 1])) // strip trailing whitespace line[--len] = '\0'; return len; } return 0; } void instance_t::read_next_directive(bool& error_flag) { char * line; std::streamsize len = read_line(line); if (len == 0 || line == NULL) return; if (! std::isspace(line[0])) error_flag = false; switch (line[0]) { case '\0': assert(false); // shouldn't ever reach here break; case ' ': case '\t': if (! error_flag) throw parse_error(_("Unexpected whitespace at beginning of line")); break; case ';': // comments case '#': case '*': case '|': break; case '-': // option setting option_directive(line); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': xact_directive(line, len); break; case '=': // automated xact automated_xact_directive(line); break; case '~': // period xact period_xact_directive(line); break; case '@': case '!': line++; // fall through... default: // some other directive if (! general_directive(line)) { switch (line[0]) { #if defined(TIMELOG_SUPPORT) case 'i': clock_in_directive(line, false); break; case 'I': clock_in_directive(line, true); break; case 'o': clock_out_directive(line, false); break; case 'O': clock_out_directive(line, true); break; case 'h': case 'b': break; #endif // TIMELOG_SUPPORT case 'A': // a default account for unbalanced posts default_account_directive(line + 1); break; case 'C': // a set of conversions price_conversion_directive(line); break; case 'D': // a default commodity for "xact" default_commodity_directive(line); break; case 'N': // don't download prices nomarket_directive(line); break; case 'P': // a pricing xact price_xact_directive(line); break; case 'Y': // set the current year if (std::strlen(line+1) == 0) throw_(parse_error, _f("Directive '%1%' requires an argument") % line[0]); apply_year_directive(line+1); break; } } break; } } #if defined(TIMELOG_SUPPORT) void instance_t::clock_in_directive(char * line, bool capitalized) { string datetime(line, 2, 19); char * p = skip_ws(line + 22); char * n = next_element(p, true); char * end = n ? next_element(n, true) : NULL; if (end && *end == ';') end = skip_ws(end + 1); else end = NULL; position_t position; position.pathname = context.pathname; position.beg_pos = context.line_beg_pos; position.beg_line = context.linenum; position.end_pos = context.curr_pos; position.end_line = context.linenum; position.sequence = context.sequence++; time_xact_t event(position, parse_datetime(datetime), capitalized, p ? top_account()->find_account(p) : NULL, n ? n : "", end ? end : ""); timelog.clock_in(event); } void instance_t::clock_out_directive(char * line, bool capitalized) { string datetime(line, 2, 19); char * p = skip_ws(line + 22); char * n = next_element(p, true); char * end = n ? next_element(n, true) : NULL; if (end && *end == ';') end = skip_ws(end + 1); else end = NULL; position_t position; position.pathname = context.pathname; position.beg_pos = context.line_beg_pos; position.beg_line = context.linenum; position.end_pos = context.curr_pos; position.end_line = context.linenum; position.sequence = context.sequence++; time_xact_t event(position, parse_datetime(datetime), capitalized, p ? top_account()->find_account(p) : NULL, n ? n : "", end ? end : ""); context.count += timelog.clock_out(event); } #endif // TIMELOG_SUPPORT void instance_t::default_commodity_directive(char * line) { amount_t amt(skip_ws(line + 1)); VERIFY(amt.valid()); commodity_pool_t::current_pool->default_commodity = &amt.commodity(); amt.commodity().add_flags(COMMODITY_KNOWN); } void instance_t::default_account_directive(char * line) { context.journal->bucket = top_account()->find_account(skip_ws(line)); context.journal->bucket->add_flags(ACCOUNT_KNOWN); } void instance_t::price_conversion_directive(char * line) { if (char * p = std::strchr(line + 1, '=')) { *p++ = '\0'; amount_t::parse_conversion(line + 1, p); } } void instance_t::price_xact_directive(char * line) { optional<std::pair<commodity_t *, price_point_t> > point = commodity_pool_t::current_pool->parse_price_directive(skip_ws(line + 1)); if (! point) throw parse_error(_("Pricing entry failed to parse")); } void instance_t::nomarket_directive(char * line) { char * p = skip_ws(line + 1); string symbol; commodity_t::parse_symbol(p, symbol); if (commodity_t * commodity = commodity_pool_t::current_pool->find_or_create(symbol)) commodity->add_flags(COMMODITY_NOMARKET | COMMODITY_KNOWN); } void instance_t::option_directive(char * line) { char * p = next_element(line); if (! p) { p = std::strchr(line, '='); if (p) *p++ = '\0'; } if (! process_option(context.pathname.string(), line + 2, *context.scope, p, line)) throw_(option_error, _f("Illegal option --%1%") % (line + 2)); } void instance_t::automated_xact_directive(char * line) { std::istream::pos_type pos = context.line_beg_pos; bool reveal_context = true; try { query_t query; keep_details_t keeper(true, true, true); expr_t::ptr_op_t expr = query.parse_args(string_value(skip_ws(line + 1)).to_sequence(), keeper, false, true); if (!expr) { throw parse_error(_("Expected predicate after '='")); } unique_ptr<auto_xact_t> ae(new auto_xact_t(predicate_t(expr, keeper))); ae->pos = position_t(); ae->pos->pathname = context.pathname; ae->pos->beg_pos = context.line_beg_pos; ae->pos->beg_line = context.linenum; ae->pos->sequence = context.sequence++; post_t * last_post = NULL; while (peek_whitespace_line()) { std::streamsize len = read_line(line); char * p = skip_ws(line); if (! *p) break; const std::size_t remlen = std::strlen(p); if (*p == ';') { item_t * item; if (last_post) item = last_post; else item = ae.get(); // This is a trailing note, and possibly a metadata info tag ae->append_note(p + 1, *context.scope, true); item->add_flags(ITEM_NOTE_ON_NEXT_LINE); item->pos->end_pos = context.curr_pos; item->pos->end_line++; } else if ((remlen > 7 && *p == 'a' && std::strncmp(p, "assert", 6) == 0 && std::isspace(p[6])) || (remlen > 6 && *p == 'c' && std::strncmp(p, "check", 5) == 0 && std::isspace(p[5])) || (remlen > 5 && *p == 'e' && ((std::strncmp(p, "expr", 4) == 0 && std::isspace(p[4])) || (std::strncmp(p, "eval", 4) == 0 && std::isspace(p[4]))))) { const char c = *p; p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]); if (! ae->check_exprs) ae->check_exprs = expr_t::check_expr_list(); ae->check_exprs->push_back (expr_t::check_expr_pair(expr_t(p), c == 'a' ? expr_t::EXPR_ASSERTION : (c == 'c' ? expr_t::EXPR_CHECK : expr_t::EXPR_GENERAL))); } else { reveal_context = false; if (post_t * post = parse_post(p, len - (p - line), top_account(), NULL, true)) { reveal_context = true; ae->add_post(post); ae->active_post = last_post = post; } reveal_context = true; } } context.journal->auto_xacts.push_back(ae.get()); ae->journal = context.journal; ae->pos->end_pos = context.curr_pos; ae->pos->end_line = context.linenum; ae.release(); } catch (const std::exception&) { if (reveal_context) { add_error_context(_("While parsing automated transaction:")); add_error_context(source_context(context.pathname, pos, context.curr_pos, "> ")); } throw; } } void instance_t::period_xact_directive(char * line) { std::istream::pos_type pos = context.line_beg_pos; bool reveal_context = true; try { unique_ptr<period_xact_t> pe(new period_xact_t(skip_ws(line + 1))); pe->pos = position_t(); pe->pos->pathname = context.pathname; pe->pos->beg_pos = context.line_beg_pos; pe->pos->beg_line = context.linenum; pe->pos->sequence = context.sequence++; reveal_context = false; if (parse_posts(top_account(), *pe.get())) { reveal_context = true; pe->journal = context.journal; if (pe->finalize()) { context.journal->extend_xact(pe.get()); context.journal->period_xacts.push_back(pe.get()); pe->pos->end_pos = context.curr_pos; pe->pos->end_line = context.linenum; pe.release(); } else { reveal_context = true; pe->journal = NULL; throw parse_error(_("Period transaction failed to balance")); } } } catch (const std::exception&) { if (reveal_context) { add_error_context(_("While parsing periodic transaction:")); add_error_context(source_context(context.pathname, pos, context.curr_pos, "> ")); } throw; } } void instance_t::xact_directive(char * line, std::streamsize len) { TRACE_START(xacts, 1, "Time spent handling transactions:"); if (xact_t * xact = parse_xact(line, len, top_account())) { unique_ptr<xact_t> manager(xact); if (context.journal->add_xact(xact)) { manager.release(); // it's owned by the journal now context.count++; } // It's perfectly valid for the journal to reject the xact, which it // will do if the xact has no substantive effect (for example, a // checking xact, all of whose postings have null amounts). } else { throw parse_error(_("Failed to parse transaction")); } TRACE_STOP(xacts, 1); } void instance_t::include_directive(char * line) { path filename; DEBUG("textual.include", "include: " << line); if (line[0] != '/' && line[0] != '\\' && line[0] != '~') { DEBUG("textual.include", "received a relative path"); DEBUG("textual.include", "parent file path: " << context.pathname); path parent_path = context.pathname.parent_path(); if (parent_path.empty()) { filename = context.current_directory / line; } else { filename = parent_path / line; DEBUG("textual.include", "normalized path: " << filename.string()); } } else { filename = line; } filename = resolve_path(filename); DEBUG("textual.include", "resolved path: " << filename.string()); mask_t glob; path parent_path = filename.parent_path(); glob.assign_glob('^' + filename.filename().string() + '$'); bool files_found = false; if (exists(parent_path)) { filesystem::directory_iterator end; // Sort parent_path since on some file systems it is unsorted. std::vector<path> sorted_parent_path; std::copy(filesystem::directory_iterator(parent_path), filesystem::directory_iterator(), std::back_inserter(sorted_parent_path)); std::sort(sorted_parent_path.begin(), sorted_parent_path.end()); for (std::vector<path>::const_iterator iter(sorted_parent_path.begin()), it_end(sorted_parent_path.end()); iter != it_end; ++iter) { if (is_regular_file(*iter)) { string base = (*iter).filename().string(); if (glob.match(base)) { journal_t * journal = context.journal; account_t * master = top_account(); scope_t * scope = context.scope; std::size_t& errors = context.errors; std::size_t& count = context.count; std::size_t& sequence = context.sequence; DEBUG("textual.include", "Including: " << *iter); DEBUG("textual.include", "Master account: " << master->fullname()); context_stack.push(*iter); context_stack.get_current().journal = journal; context_stack.get_current().master = master; context_stack.get_current().scope = scope; try { instance_t instance(context_stack, context_stack.get_current(), this, no_assertions); instance.apply_stack.push_front(application_t("account", master)); instance.parse(); } catch (...) { errors += context_stack.get_current().errors; count += context_stack.get_current().count; sequence += context_stack.get_current().sequence; context_stack.pop(); throw; } errors += context_stack.get_current().errors; count += context_stack.get_current().count; sequence += context_stack.get_current().sequence; context_stack.pop(); files_found = true; } } } } if (! files_found) throw_(std::runtime_error, _f("File to include was not found: %1%") % filename); } void instance_t::apply_directive(char * line) { char * b = next_element(line); string keyword(line); if (! b) throw_(parse_error, _f("Directive 'apply %1%' requires an argument") % keyword); if (keyword == "account") apply_account_directive(b); else if (keyword == "tag") apply_tag_directive(b); else if (keyword == "fixed" || keyword == "rate") apply_rate_directive(b); else if (keyword == "year") apply_year_directive(b); } void instance_t::apply_account_directive(char * line) { if (account_t * acct = top_account()->find_account(line)) apply_stack.push_front(application_t("account", acct)); #if !NO_ASSERTS else assert("Failed to create account" == NULL); #endif } void instance_t::apply_tag_directive(char * line) { string tag(trim_ws(line)); if (tag.find(':') == string::npos) tag = string(":") + tag + ":"; apply_stack.push_front(application_t("tag", tag)); } void instance_t::apply_rate_directive(char * line) { if (optional<std::pair<commodity_t *, price_point_t> > price_point = commodity_pool_t::current_pool->parse_price_directive (trim_ws(line), true, true)) { apply_stack.push_front (application_t("fixed", fixed_rate_t(price_point->first, price_point->second.price))); } else { throw_(std::runtime_error, _("Error in fixed directive")); } } void instance_t::apply_year_directive(char * line) { try { unsigned short year(lexical_cast<unsigned short>(skip_ws(line))); apply_stack.push_front(application_t("year", epoch)); DEBUG("times.epoch", "Setting current year to " << year); // This must be set to the last day of the year, otherwise partial // dates like "11/01" will refer to last year's November, not the // current year. epoch = datetime_t(date_t(year, 12, 31)); } catch(bad_lexical_cast &) { throw_(parse_error, _f("Argument '%1%' not a valid year") % skip_ws(line)); } } void instance_t::end_apply_directive(char * kind) { char * b = kind ? next_element(kind) : NULL; string name(b ? b : ""); if (apply_stack.size() <= 1) { if (name.empty()) { throw_(std::runtime_error, _("'end' or 'end apply' found, but no enclosing 'apply' directive")); } else { throw_(std::runtime_error, _f("'end apply %1%' found, but no enclosing 'apply' directive") % name); } } if (! name.empty() && name != apply_stack.front().label) throw_(std::runtime_error, _f("'end apply %1%' directive does not match 'apply %2%' directive") % name % apply_stack.front().label); if (apply_stack.front().value.type() == typeid(optional<datetime_t>)) epoch = boost::get<optional<datetime_t> >(apply_stack.front().value); apply_stack.pop_front(); } void instance_t::account_directive(char * line) { std::istream::pos_type beg_pos = context.line_beg_pos; std::size_t beg_linenum = context.linenum; char * p = skip_ws(line); account_t * account = context.journal->register_account(p, NULL, top_account()); unique_ptr<auto_xact_t> ae; while (peek_whitespace_line()) { read_line(line); char * q = skip_ws(line); if (! *q) break; char * b = next_element(q); string keyword(q); // Ensure there's an argument for the directives that need one. if (! b && keyword != "default") throw_(parse_error, _f("Account directive '%1%' requires an argument") % keyword); if (keyword == "alias") { account_alias_directive(account, b); } else if (keyword == "payee") { account_payee_directive(account, b); } else if (keyword == "value") { account_value_directive(account, b); } else if (keyword == "default") { account_default_directive(account); } else if (keyword == "assert" || keyword == "check") { keep_details_t keeper(true, true, true); expr_t expr(string("account == \"") + account->fullname() + "\""); predicate_t pred(expr.get_op(), keeper); if (! ae.get()) { ae.reset(new auto_xact_t(pred)); ae->pos = position_t(); ae->pos->pathname = context.pathname; ae->pos->beg_pos = beg_pos; ae->pos->beg_line = beg_linenum; ae->pos->sequence = context.sequence++; ae->check_exprs = expr_t::check_expr_list(); } ae->check_exprs->push_back (expr_t::check_expr_pair(expr_t(b), keyword == "assert" ? expr_t::EXPR_ASSERTION : expr_t::EXPR_CHECK)); } else if (keyword == "eval" || keyword == "expr") { // jww (2012-02-27): Make account into symbol scopes so that this // can be used to override definitions within the account. bind_scope_t bound_scope(*context.scope, *account); expr_t(b).calc(bound_scope); } else if (keyword == "note") { account->note = b; } } if (ae.get()) { context.journal->auto_xacts.push_back(ae.get()); ae->journal = context.journal; ae->pos->end_pos = in.tellg(); ae->pos->end_line = context.linenum; ae.release(); } } void instance_t::account_alias_directive(account_t * account, string alias) { // Once we have an alias name (alias) and the target account // (account), add a reference to the account in the `account_aliases' // map, which is used by the post parser to resolve alias references. trim(alias); // Ensure that no alias like "alias Foo=Foo" is registered. if ( alias == account->fullname()) { throw_(parse_error, _f("Illegal alias %1%=%2%") % alias % account->fullname()); } std::pair<accounts_map::iterator, bool> result = context.journal->account_aliases.insert (accounts_map::value_type(alias, account)); if (! result.second) (*result.first).second = account; } void instance_t::alias_directive(char * line) { if (char * e = std::strchr(line, '=')) { char * z = e - 1; while (std::isspace(*z)) *z-- = '\0'; *e++ = '\0'; e = skip_ws(e); account_alias_directive(top_account()->find_account(e), line); } } void instance_t::account_payee_directive(account_t * account, string payee) { trim(payee); context.journal->payees_for_unknown_accounts .push_back(account_mapping_t(mask_t(payee), account)); } void instance_t::account_default_directive(account_t * account) { context.journal->bucket = account; } void instance_t::account_value_directive(account_t * account, string expr_str) { account->value_expr = expr_t(expr_str); } void instance_t::payee_directive(char * line) { string payee = context.journal->register_payee(line); while (peek_whitespace_line()) { read_line(line); char * p = skip_ws(line); if (! *p) break; char * b = next_element(p); string keyword(p); if (! b) throw_(parse_error, _f("Payee directive '%1%' requires an argument") % keyword); if (keyword == "alias") payee_alias_directive(payee, b); else if (keyword == "uuid") payee_uuid_directive(payee, b); } } void instance_t::payee_alias_directive(const string& payee, string alias) { trim(alias); context.journal->payee_alias_mappings .push_back(payee_alias_mapping_t(mask_t(alias), payee)); } void instance_t::payee_uuid_directive(const string& payee, string uuid) { trim(uuid); context.journal->payee_uuid_mappings .push_back(payee_uuid_mapping_t(uuid, payee)); } void instance_t::commodity_directive(char * line) { char * p = skip_ws(line); string symbol; commodity_t::parse_symbol(p, symbol); if (commodity_t * commodity = commodity_pool_t::current_pool->find_or_create(symbol)) { context.journal->register_commodity(*commodity, 0); while (peek_whitespace_line()) { read_line(line); char * q = skip_ws(line); if (! *q) break; char * b = next_element(q); string keyword(q); // Ensure there's an argument for the directives that need one. if (! b && keyword != "nomarket" && keyword != "default") throw_(parse_error, _f("Commodity directive '%1%' requires an argument") % keyword); if (keyword == "alias") commodity_alias_directive(*commodity, b); else if (keyword == "value") commodity_value_directive(*commodity, b); else if (keyword == "format") commodity_format_directive(*commodity, b); else if (keyword == "nomarket") commodity_nomarket_directive(*commodity); else if (keyword == "default") commodity_default_directive(*commodity); else if (keyword == "note") commodity->set_note(string(b)); } } } void instance_t::commodity_alias_directive(commodity_t& comm, string alias) { trim(alias); commodity_pool_t::current_pool->alias(alias, comm); } void instance_t::commodity_value_directive(commodity_t& comm, string expr_str) { comm.set_value_expr(expr_t(expr_str)); } void instance_t::commodity_format_directive(commodity_t& comm, string format) { // jww (2012-02-27): A format specified this way should turn off // observational formatting. trim(format); amount_t amt; amt.parse(format, PARSE_NO_REDUCE); if (amt.commodity() != comm) throw_(parse_error, _f("commodity directive symbol %1% and format directive symbol %2% should be the same") % comm.symbol() % amt.commodity().symbol()); amt.commodity().add_flags(COMMODITY_STYLE_NO_MIGRATE); VERIFY(amt.valid()); } void instance_t::commodity_nomarket_directive(commodity_t& comm) { comm.add_flags(COMMODITY_NOMARKET); } void instance_t::commodity_default_directive(commodity_t& comm) { commodity_pool_t::current_pool->default_commodity = &comm; } void instance_t::tag_directive(char * line) { char * p = skip_ws(line); context.journal->register_metadata(p, NULL_VALUE, 0); while (peek_whitespace_line()) { read_line(line); char * q = skip_ws(line); if (! *q) break; char * b = next_element(q); string keyword(q); if (keyword == "assert" || keyword == "check") { context.journal->tag_check_exprs.insert (tag_check_exprs_map::value_type (string(p), expr_t::check_expr_pair(expr_t(b), keyword == "assert" ? expr_t::EXPR_ASSERTION : expr_t::EXPR_CHECK))); } } } void instance_t::eval_directive(char * line) { expr_t expr(line); expr.calc(*context.scope); } void instance_t::assert_directive(char * line) { expr_t expr(line); if (! expr.calc(*context.scope).to_boolean()) throw_(parse_error, _f("Assertion failed: %1%") % line); } void instance_t::check_directive(char * line) { expr_t expr(line); if (! expr.calc(*context.scope).to_boolean()) context.warning(_f("Check failed: %1%") % line); } void instance_t::value_directive(char * line) { context.journal->value_expr = expr_t(line); } void instance_t::comment_directive(char * line) { while (in.good() && ! in.eof()) { if (read_line(line) > 0) { std::string buf(line); if (starts_with(buf, "end comment") || starts_with(buf, "end test")) break; } } } #if HAVE_BOOST_PYTHON void instance_t::import_directive(char * line) { string module_name(line); trim(module_name); python_session->import_option(module_name); } void instance_t::python_directive(char * line) { std::ostringstream script; if (line) script << skip_ws(line) << '\n'; std::size_t indent = 0; while (peek_whitespace_line() || peek_blank_line()) { if (read_line(line) > 0) { if (! indent) { const char * p = line; while (*p && std::isspace(*p)) { ++indent; ++p; } } const char * p = line; for (std::size_t i = 0; i < indent; i++) { if (std::isspace(*p)) ++p; else break; } if (*p) script << p << '\n'; } } if (! python_session->is_initialized) python_session->initialize(); python_session->main_module->define_global ("journal", python::object(python::ptr(context.journal))); python_session->eval(script.str(), python_interpreter_t::PY_EVAL_MULTI); } #else void instance_t::import_directive(char *) { throw_(parse_error, _("'import' directive seen, but Python support is missing")); } void instance_t::python_directive(char *) { throw_(parse_error, _("'python' directive seen, but Python support is missing")); } #endif // HAVE_BOOST_PYTHON bool instance_t::general_directive(char * line) { char buf[8192]; std::strcpy(buf, line); char * p = buf; char * arg = next_element(buf); if (*p == '@' || *p == '!') p++; // Ensure there's an argument for all directives that need one. if (! arg && std::strcmp(p, "comment") != 0 && std::strcmp(p, "end") != 0 && std::strcmp(p, "python") != 0 && std::strcmp(p, "test") != 0 && *p != 'Y') { throw_(parse_error, _f("Directive '%1%' requires an argument") % p); } switch (*p) { case 'a': if (std::strcmp(p, "account") == 0) { account_directive(arg); return true; } else if (std::strcmp(p, "alias") == 0) { alias_directive(arg); return true; } else if (std::strcmp(p, "apply") == 0) { apply_directive(arg); return true; } else if (std::strcmp(p, "assert") == 0) { assert_directive(arg); return true; } break; case 'b': if (std::strcmp(p, "bucket") == 0) { default_account_directive(arg); return true; } break; case 'c': if (std::strcmp(p, "check") == 0) { check_directive(arg); return true; } else if (std::strcmp(p, "comment") == 0) { comment_directive(arg); return true; } else if (std::strcmp(p, "commodity") == 0) { commodity_directive(arg); return true; } break; case 'd': if (std::strcmp(p, "def") == 0 || std::strcmp(p, "define") == 0) { eval_directive(arg); return true; } break; case 'e': if (std::strcmp(p, "end") == 0) { end_apply_directive(arg); return true; } else if (std::strcmp(p, "expr") == 0 || std::strcmp(p, "eval") == 0) { eval_directive(arg); return true; } break; case 'i': if (std::strcmp(p, "include") == 0) { include_directive(arg); return true; } else if (std::strcmp(p, "import") == 0) { import_directive(arg); return true; } break; case 'p': if (std::strcmp(p, "payee") == 0) { payee_directive(arg); return true; } else if (std::strcmp(p, "python") == 0) { python_directive(arg); return true; } break; case 't': if (std::strcmp(p, "tag") == 0) { tag_directive(arg); return true; } else if (std::strcmp(p, "test") == 0) { comment_directive(arg); return true; } break; case 'v': if (std::strcmp(p, "value") == 0) { value_directive(arg); return true; } break; case 'y': if (std::strcmp(p, "year") == 0) { apply_year_directive(arg); return true; } break; } if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) { call_scope_t args(*this); args.push_back(string_value(p)); op->as_function()(args); return true; } return false; } post_t * instance_t::parse_post(char * line, std::streamsize len, account_t * account, xact_t * xact, bool defer_expr) { TRACE_START(post_details, 1, "Time spent parsing postings:"); unique_ptr<post_t> post(new post_t); post->xact = xact; // this could be NULL post->pos = position_t(); post->pos->pathname = context.pathname; post->pos->beg_pos = context.line_beg_pos; post->pos->beg_line = context.linenum; post->pos->sequence = context.sequence++; char buf[parse_context_t::MAX_LINE + 1]; std::strcpy(buf, line); std::streamsize beg = 0; try { // Parse the state flag assert(line); assert(*line); char * p = skip_ws(line); switch (*p) { case '*': post->set_state(item_t::CLEARED); p = skip_ws(p + 1); DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed the CLEARED flag"); break; case '!': post->set_state(item_t::PENDING); p = skip_ws(p + 1); DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed the PENDING flag"); break; } if (xact && (xact->_state != item_t::UNCLEARED && post->_state == item_t::UNCLEARED)) post->set_state(xact->_state); // Parse the account name if (! *p || *p == ';') throw parse_error(_("Posting has no account")); char * next = next_element(p, true); char * e = p + std::strlen(p); while (e > p && std::isspace(*(e - 1))) e--; if ((*p == '[' && *(e - 1) == ']') || (*p == '(' && *(e - 1) == ')')) { post->add_flags(POST_VIRTUAL); DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed a virtual account name"); if (*p == '[') { post->add_flags(POST_MUST_BALANCE); DEBUG("textual.parse", "line " << context.linenum << ": " << "Posting must balance"); } p++; e--; } else if (*p == '<' && *(e - 1) == '>') { post->add_flags(POST_DEFERRED); DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed a deferred account name"); p++; e--; } string name(p, static_cast<string::size_type>(e - p)); DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed account name " << name); post->account = context.journal->register_account(name, post.get(), account); // Parse the optional amount if (next && *next && (*next != ';' && *next != '=')) { beg = static_cast<std::streamsize>(next - line); ptristream stream(next, static_cast<std::size_t>(len - beg)); if (*next != '(') // indicates a value expression post->amount.parse(stream, PARSE_NO_REDUCE); else parse_amount_expr(stream, *context.scope, *post.get(), post->amount, PARSE_NO_REDUCE | PARSE_SINGLE | PARSE_NO_ASSIGN, defer_expr, &post->amount_expr); DEBUG("textual.parse", "line " << context.linenum << ": " << "post amount = " << post->amount); if (! post->amount.is_null() && post->amount.has_commodity()) { context.journal->register_commodity(post->amount.commodity(), post.get()); if (! post->amount.has_annotation()) { std::vector<fixed_rate_t> rates; get_applications<fixed_rate_t>(rates); foreach (fixed_rate_t& rate, rates) { if (*rate.first == post->amount.commodity()) { annotation_t details(rate.second); details.add_flags(ANNOTATION_PRICE_FIXATED); post->amount.annotate(details); DEBUG("textual.parse", "line " << context.linenum << ": " << "applied rate = " << post->amount); break; } } } } if (stream.eof()) { next = NULL; } else { next = skip_ws(next + static_cast<std::ptrdiff_t>(stream.tellg())); // Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST) if (*next == '@' || (*next == '(' && *(next + 1) == '@')) { DEBUG("textual.parse", "line " << context.linenum << ": " << "Found a price indicator"); if (*next == '(') { post->add_flags(POST_COST_VIRTUAL); ++next; } bool per_unit = true; if (*++next == '@') { per_unit = false; post->add_flags(POST_COST_IN_FULL); DEBUG("textual.parse", "line " << context.linenum << ": " << "And it's for a total price"); next++; } if (post->has_flags(POST_COST_VIRTUAL) && *next == ')') ++next; p = skip_ws(next); if (*p) { post->cost = amount_t(); bool fixed_cost = false; if (*p == '=') { p++; fixed_cost = true; if (*p == '\0') throw parse_error(_("Posting is missing a cost amount")); } beg = static_cast<std::streamsize>(p - line); ptristream cstream(p, static_cast<std::size_t>(len - beg)); if (*p != '(') // indicates a value expression post->cost->parse(cstream, PARSE_NO_MIGRATE); else parse_amount_expr(cstream, *context.scope, *post.get(), *post->cost, PARSE_NO_MIGRATE | PARSE_SINGLE | PARSE_NO_ASSIGN); if (post->cost->sign() < 0) throw parse_error(_("A posting's cost may not be negative")); post->cost->in_place_unround(); if (per_unit) { // For the sole case where the cost might be uncommoditized, // guarantee that the commodity of the cost after multiplication // is the same as it was before. commodity_t& cost_commodity(post->cost->commodity()); *post->cost *= post->amount; post->cost->set_commodity(cost_commodity); } else if (post->amount.sign() < 0) { post->cost->in_place_negate(); } if (fixed_cost) post->add_flags(POST_COST_FIXATED); post->given_cost = post->cost; DEBUG("textual.parse", "line " << context.linenum << ": " << "Total cost is " << *post->cost); DEBUG("textual.parse", "line " << context.linenum << ": " << "Annotated amount is " << post->amount); if (cstream.eof()) next = NULL; else next = skip_ws(p + static_cast<std::ptrdiff_t>(cstream.tellg())); } else { throw parse_error(_("Expected a cost amount")); } } } } // Parse the optional balance assignment if (xact && next && *next == '=') { DEBUG("textual.parse", "line " << context.linenum << ": " << "Found a balance assignment indicator"); beg = static_cast<std::streamsize>(++next - line); p = skip_ws(next); if (*p) { post->assigned_amount = amount_t(); beg = static_cast<std::streamsize>(p - line); ptristream stream(p, static_cast<std::size_t>(len - beg)); if (*p != '(') // indicates a value expression post->assigned_amount->parse(stream, PARSE_NO_MIGRATE); else parse_amount_expr(stream, *context.scope, *post.get(), *post->assigned_amount, PARSE_SINGLE | PARSE_NO_MIGRATE); if (post->assigned_amount->is_null()) { if (post->amount.is_null()) throw parse_error(_("Balance assignment must evaluate to a constant")); else throw parse_error(_("Balance assertion must evaluate to a constant")); } DEBUG("textual.parse", "line " << context.linenum << ": " << "POST assign: parsed balance amount = " << *post->assigned_amount); const amount_t& amt(*post->assigned_amount); value_t account_total (post->account->amount(!post->has_flags(POST_VIRTUAL)).strip_annotations(keep_details_t())); DEBUG("post.assign", "line " << context.linenum << ": " << "account balance = " << account_total); DEBUG("post.assign", "line " << context.linenum << ": " << "post amount = " << amt << " (is_zero = " << amt.is_zero() << ")"); balance_t diff = amt; switch (account_total.type()) { case value_t::AMOUNT: { amount_t amt(account_total.as_amount().strip_annotations(keep_details_t())); diff -= amt; DEBUG("textual.parse", "line " << context.linenum << ": " << "Subtracting amount " << amt << " from diff, yielding " << diff); break; } case value_t::BALANCE: { balance_t bal(account_total.as_balance().strip_annotations(keep_details_t())); diff -= bal; DEBUG("textual.parse", "line " << context.linenum << ": " << "Subtracting balance " << bal << " from diff, yielding " << diff); break; } default: break; } DEBUG("post.assign", "line " << context.linenum << ": " << "diff = " << diff); DEBUG("textual.parse", "line " << context.linenum << ": " << "POST assign: diff = " << diff); // Subtract amounts from previous posts to this account in the xact. for (post_t* p : xact->posts) { if (p->account == post->account && p->has_flags(POST_VIRTUAL) == post->has_flags(POST_VIRTUAL)) { amount_t amt(p->amount.strip_annotations(keep_details_t())); diff -= amt; DEBUG("textual.parse", "line " << context.linenum << ": " << "Subtracting " << amt << ", diff = " << diff); } } // If amt has a commodity, restrict balancing to that. Otherwise, it's the blanket '0' and // check that all of them are zero. if (amt.has_commodity()) { DEBUG("textual.parse", "line " << context.linenum << ": " << "Finding commodity " << amt.commodity() << " (" << amt << ") in balance " << diff); optional<amount_t> wanted_commodity = diff.commodity_amount(amt.commodity()); if (!wanted_commodity) { diff = amt - amt; // this is '0' with the correct commodity. } else { diff = *wanted_commodity; } DEBUG("textual.parse", "line " << context.linenum << ": " << "Diff is now " << diff); } if (post->amount.is_null()) { // balance assignment if (! diff.is_zero()) { // This will fail if there are more than 1 commodity in diff, which is wanted, // as amount cannot store more than 1 commodity. post->amount = diff.to_amount(); DEBUG("textual.parse", "line " << context.linenum << ": " << "Overwrite null posting with " << diff.to_amount()); } else { post->amount = amt - amt; // this is '0' with the correct commodity. DEBUG("textual.parse", "line " << context.linenum << ": " << "Overwrite null posting with zero diff with " << amt - amt); } } else { // balance assertion diff -= post->amount.strip_annotations(keep_details_t()); if (! no_assertions && ! diff.is_zero()) { balance_t tot = (-diff + amt).strip_annotations(keep_details_t()); DEBUG("textual.parse", "Balance assertion: off by " << diff << " (expected to see " << tot << ")"); throw_(parse_error, _f("Balance assertion off by %1% (expected to see %2%)") % diff.to_string() % tot.to_string()); } } if (stream.eof()) next = NULL; else next = skip_ws(p + static_cast<std::ptrdiff_t>(stream.tellg())); } else { throw parse_error(_("Expected an balance assignment/assertion amount")); } } // Parse the optional note if (next && *next == ';') { post->append_note(++next, *context.scope, true); next = line + len; DEBUG("textual.parse", "line " << context.linenum << ": " << "Parsed a posting note"); } // There should be nothing more to read if (next && *next) throw_(parse_error, _f("Unexpected char '%1%' (Note: inline math requires parentheses)") % *next); post->pos->end_pos = context.curr_pos; post->pos->end_line = context.linenum; std::vector<string> tags; get_applications<string>(tags); foreach (string& tag, tags) post->parse_tags(tag.c_str(), *context.scope, true); string post_payee = post->payee_from_tag(); if (post_payee != "") post->set_payee(context.journal->validate_payee(post_payee)); TRACE_STOP(post_details, 1); return post.release(); } catch (const std::exception&) { add_error_context(_("While parsing posting:")); add_error_context(line_context(buf, static_cast<string::size_type>(beg), static_cast<string::size_type>(len))); throw; } } bool instance_t::parse_posts(account_t * account, xact_base_t& xact, const bool defer_expr) { TRACE_START(xact_posts, 1, "Time spent parsing postings:"); bool added = false; while (peek_whitespace_line()) { char * line; std::streamsize len = read_line(line); char * p = skip_ws(line); if (*p != ';') { if (post_t * post = parse_post(line, len, account, NULL, defer_expr)) { xact.add_post(post); added = true; } } } TRACE_STOP(xact_posts, 1); return added; } xact_t * instance_t::parse_xact(char * line, std::streamsize len, account_t * account) { TRACE_START(xact_text, 1, "Time spent parsing transaction text:"); unique_ptr<xact_t> xact(new xact_t); xact->pos = position_t(); xact->pos->pathname = context.pathname; xact->pos->beg_pos = context.line_beg_pos; xact->pos->beg_line = context.linenum; xact->pos->sequence = context.sequence++; bool reveal_context = true; try { // Parse the date char * next = next_element(line); if (char * p = std::strchr(line, '=')) { *p++ = '\0'; xact->_date_aux = parse_date(p); } xact->_date = parse_date(line); // Parse the optional cleared flag: * if (next) { switch (*next) { case '*': xact->_state = item_t::CLEARED; next = skip_ws(++next); break; case '!': xact->_state = item_t::PENDING; next = skip_ws(++next); break; } } // Parse the optional code: (TEXT) if (next && *next == '(') { if (char * p = std::strchr(next++, ')')) { *p++ = '\0'; xact->code = next; next = skip_ws(p); } } // Parse the description text if (next && *next) { char * p = next; std::size_t spaces = 0; std::size_t tabs = 0; while (*p) { if (*p == ' ') { ++spaces; } else if (*p == '\t') { ++tabs; } else if (*p == ';' && (tabs > 0 || spaces > 1)) { char *q = p - 1; while (q > next && std::isspace(*q)) --q; if (q >= next) *(q + 1) = '\0'; break; } else { spaces = 0; tabs = 0; } ++p; } xact->payee = context.journal->validate_payee(next); next = p; } else { xact->payee = _("<Unspecified payee>"); } // Parse the xact note if (next && *next == ';') xact->append_note(++next, *context.scope, false); TRACE_STOP(xact_text, 1); // Parse all of the posts associated with this xact TRACE_START(xact_details, 1, "Time spent parsing transaction details:"); post_t * last_post = NULL; while (peek_whitespace_line()) { len = read_line(line); char * p = skip_ws(line); if (! *p) break; const std::size_t remlen = std::strlen(p); item_t * item; if (last_post) item = last_post; else item = xact.get(); if (*p == ';') { // This is a trailing note, and possibly a metadata info tag item->append_note(p + 1, *context.scope, true); item->add_flags(ITEM_NOTE_ON_NEXT_LINE); item->pos->end_pos = context.curr_pos; item->pos->end_line++; } else if ((remlen > 7 && *p == 'a' && std::strncmp(p, "assert", 6) == 0 && std::isspace(p[6])) || (remlen > 6 && *p == 'c' && std::strncmp(p, "check", 5) == 0 && std::isspace(p[5])) || (remlen > 5 && *p == 'e' && std::strncmp(p, "expr", 4) == 0 && std::isspace(p[4]))) { const char c = *p; p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]); expr_t expr(p); bind_scope_t bound_scope(*context.scope, *item); if (c == 'e') { expr.calc(bound_scope); } else if (! expr.calc(bound_scope).to_boolean()) { if (c == 'a') { throw_(parse_error, _f("Transaction assertion failed: %1%") % p); } else { context.warning(_f("Transaction check failed: %1%") % p); } } } else { reveal_context = false; if (!last_post) { if (xact->has_tag(_("UUID"))) { string uuid = xact->get_tag(_("UUID"))->to_string(); foreach (payee_uuid_mapping_t value, context.journal->payee_uuid_mappings) { if (value.first.compare(uuid) == 0) { xact->payee = value.second; } } } } if (post_t * post = parse_post(p, len - (p - line), account, xact.get())) { reveal_context = true; xact->add_post(post); last_post = post; } reveal_context = true; } } #if 0 if (xact->_state == item_t::UNCLEARED) { item_t::application_t result = item_t::CLEARED; foreach (post_t * post, xact->posts) { if (post->_state == item_t::UNCLEARED) { result = item_t::UNCLEARED; break; } else if (post->_state == item_t::PENDING) { result = item_t::PENDING; } } } #endif xact->pos->end_pos = context.curr_pos; xact->pos->end_line = context.linenum; std::vector<string> tags; get_applications<string>(tags); foreach (string& tag, tags) xact->parse_tags(tag.c_str(), *context.scope, false); TRACE_STOP(xact_details, 1); return xact.release(); } catch (const std::exception&) { if (reveal_context) { add_error_context(_("While parsing transaction:")); add_error_context(source_context(xact->pos->pathname, xact->pos->beg_pos, context.curr_pos, "> ")); } throw; } } expr_t::ptr_op_t instance_t::lookup(const symbol_t::kind_t kind, const string& name) { return context.scope->lookup(kind, name); } std::size_t journal_t::read_textual(parse_context_stack_t& context_stack) { TRACE_START(parsing_total, 1, "Total time spent parsing text:"); { instance_t instance(context_stack, context_stack.get_current(), NULL, checking_style == journal_t::CHECK_PERMISSIVE); instance.apply_stack.push_front (application_t("account", context_stack.get_current().master)); instance.parse(); } TRACE_STOP(parsing_total, 1); // Apply any deferred postings at this time master->apply_deferred_posts(); // These tracers were started in textual.cc TRACE_FINISH(xact_text, 1); TRACE_FINISH(xact_details, 1); TRACE_FINISH(xact_posts, 1); TRACE_FINISH(xacts, 1); TRACE_FINISH(instance_parse, 1); // report per-instance timers TRACE_FINISH(parsing_total, 1); if (context_stack.get_current().errors > 0) throw error_count(context_stack.get_current().errors, context_stack.get_current().last); return context_stack.get_current().count; } } // namespace ledger ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/timelog.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000014702�14411236400�0015516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "timelog.h" #include "xact.h" #include "post.h" #include "account.h" #include "journal.h" #include "context.h" namespace ledger { namespace { void create_timelog_xact(const time_xact_t& in_event, const time_xact_t& out_event, parse_context_t& context) { unique_ptr<xact_t> curr(new xact_t); curr->_date = in_event.checkin.date(); curr->code = out_event.desc; // if it wasn't used above curr->payee = in_event.desc; curr->pos = in_event.position; if (! in_event.note.empty()) curr->append_note(in_event.note.c_str(), *context.scope); char buf[32]; std::snprintf(buf, 32, "%lds", long((out_event.checkin - in_event.checkin) .total_seconds())); amount_t amt; amt.parse(buf); VERIFY(amt.valid()); post_t * post = new post_t(in_event.account, amt, POST_VIRTUAL); post->set_state(out_event.completed ? item_t::CLEARED : item_t::UNCLEARED); post->pos = in_event.position; post->checkin = in_event.checkin; post->checkout = out_event.checkin; curr->add_post(post); in_event.account->add_post(post); if (! context.journal->add_xact(curr.get())) throw parse_error(_("Failed to record 'out' timelog transaction")); else curr.release(); } std::size_t clock_out_from_timelog(std::list<time_xact_t>& time_xacts, time_xact_t out_event, parse_context_t& context) { time_xact_t event; if (time_xacts.size() == 1) { event = time_xacts.back(); time_xacts.clear(); } else if (time_xacts.empty()) { throw parse_error(_("Timelog check-out event without a check-in")); } else if (! out_event.account) { throw parse_error (_("When multiple check-ins are active, checking out requires an account")); } else { bool found = false; for (std::list<time_xact_t>::iterator i = time_xacts.begin(); i != time_xacts.end(); i++) if (out_event.account == (*i).account) { event = *i; found = true; time_xacts.erase(i); break; } if (! found) throw parse_error (_("Timelog check-out event does not match any current check-ins")); } if (event.checkin.is_not_a_date_time()) throw parse_error(_("Timelog check-in has no corresponding check-out")); if (out_event.checkin.is_not_a_date_time()) throw parse_error(_("Timelog check-out has no corresponding check-in")); if (out_event.checkin < event.checkin) throw parse_error (_("Timelog check-out date less than corresponding check-in")); if (! out_event.desc.empty() && event.desc.empty()) { event.desc = out_event.desc; out_event.desc = empty_string; } if (! out_event.note.empty() && event.note.empty()) event.note = out_event.note; if (! context.journal->day_break) { create_timelog_xact(event, out_event, context); return 1; } else { time_xact_t begin(event); std::size_t xact_count = 0; while (begin.checkin < out_event.checkin) { DEBUG("timelog", "begin.checkin: " << begin.checkin); datetime_t days_end(begin.checkin.date(), time_duration_t(23, 59, 59)); days_end += seconds(1); DEBUG("timelog", "days_end: " << days_end); if (out_event.checkin <= days_end) { create_timelog_xact(begin, out_event, context); ++xact_count; break; } else { time_xact_t end(out_event); end.checkin = days_end; DEBUG("timelog", "end.checkin: " << end.checkin); create_timelog_xact(begin, end, context); ++xact_count; begin.checkin = end.checkin; } } return xact_count; } } } // unnamed namespace void time_log_t::close() { if (! time_xacts.empty()) { std::list<account_t *> accounts; foreach (time_xact_t& time_xact, time_xacts) accounts.push_back(time_xact.account); foreach (account_t * account, accounts) { DEBUG("timelog", "Clocking out from account " << account->fullname()); context.count += clock_out_from_timelog (time_xacts, time_xact_t(none, CURRENT_TIME(), false, account), context); } assert(time_xacts.empty()); } } void time_log_t::clock_in(time_xact_t event) { if (! time_xacts.empty()) { foreach (time_xact_t& time_xact, time_xacts) { if (event.account == time_xact.account) throw parse_error(_("Cannot double check-in to the same account")); } } time_xacts.push_back(event); } std::size_t time_log_t::clock_out(time_xact_t event) { if (time_xacts.empty()) throw std::logic_error(_("Timelog check-out event without a check-in")); return clock_out_from_timelog(time_xacts, event, context); } } // namespace ledger ��������������������������������������������������������������ledger-3.3.2/src/timelog.h��������������������������������������������������������������������������0000664�0000000�0000000�00000006572�14411236400�0015366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file timelog.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_TIMELOG_H #define INCLUDED_TIMELOG_H #include "utils.h" #include "times.h" #include "item.h" namespace ledger { class account_t; class journal_t; class parse_context_t; class time_xact_t { public: datetime_t checkin; bool completed; account_t * account; string desc; string note; position_t position; time_xact_t() : account(NULL) { TRACE_CTOR(time_xact_t, ""); } time_xact_t(const optional<position_t>& _position, const datetime_t& _checkin, const bool _completed = false, account_t * _account = NULL, const string& _desc = "", const string& _note = "") : checkin(_checkin), completed(_completed), account(_account), desc(_desc), note(_note), position(_position ? *_position : position_t()) { TRACE_CTOR(time_xact_t, "position_t, datetime_t, bool, account_t *, string, string"); } time_xact_t(const time_xact_t& xact) : checkin(xact.checkin), completed(xact.completed), account(xact.account), desc(xact.desc), note(xact.note), position(xact.position) { TRACE_CTOR(time_xact_t, "copy"); } ~time_xact_t() throw() { TRACE_DTOR(time_xact_t); } }; class time_log_t : public boost::noncopyable { std::list<time_xact_t> time_xacts; parse_context_t& context; public: time_log_t(parse_context_t& _context) : context(_context) { TRACE_CTOR(time_log_t, "parse_context_t&"); } ~time_log_t() { TRACE_DTOR(time_log_t); } void clock_in(time_xact_t event); std::size_t clock_out(time_xact_t event); void close(); }; } // namespace ledger #endif // INCLUDED_TIMELOG_H ��������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/times.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000151313�14411236400�0015177�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "times.h" #if defined(_WIN32) || defined(__CYGWIN__) #include "strptime.h" #endif namespace ledger { optional<datetime_t> epoch; date_time::weekdays start_of_week = gregorian::Sunday; namespace { template <typename T, typename InputFacetType, typename OutputFacetType> class temporal_io_t : public noncopyable { string fmt_str; public: date_traits_t traits; bool input; temporal_io_t(const char * _fmt_str, bool _input) : fmt_str(_fmt_str), traits(icontains(fmt_str, "%F") || icontains(fmt_str, "%y"), icontains(fmt_str, "%F") || icontains(fmt_str, "%m") || icontains(fmt_str, "%b"), icontains(fmt_str, "%F") || icontains(fmt_str, "%d")), input(_input) { } void set_format(const char * fmt) { fmt_str = fmt; traits = date_traits_t(icontains(fmt_str, "%F") || icontains(fmt_str, "%y"), icontains(fmt_str, "%F") || icontains(fmt_str, "%m") || icontains(fmt_str, "%b"), icontains(fmt_str, "%F") || icontains(fmt_str, "%d")); } T parse(const char *) {} std::string format(const T& when) { std::tm data(to_tm(when)); char buf[128]; std::strftime(buf, 127, fmt_str.c_str(), &data); return buf; } }; template <> datetime_t temporal_io_t<datetime_t, posix_time::time_input_facet, posix_time::time_facet> ::parse(const char * str) { std::tm data; std::memset(&data, 0, sizeof(std::tm)); if (strptime(str, fmt_str.c_str(), &data)) return posix_time::ptime_from_tm(data); else return datetime_t(); } template <> date_t temporal_io_t<date_t, gregorian::date_input_facet, gregorian::date_facet> ::parse(const char * str) { std::tm data; std::memset(&data, 0, sizeof(std::tm)); data.tm_year = CURRENT_DATE().year() - 1900; data.tm_mday = 1; // some formats have no day if (strptime(str, fmt_str.c_str(), &data)) return gregorian::date_from_tm(data); else return date_t(); } typedef temporal_io_t<datetime_t, posix_time::time_input_facet, posix_time::time_facet> datetime_io_t; typedef temporal_io_t<date_t, gregorian::date_input_facet, gregorian::date_facet> date_io_t; shared_ptr<datetime_io_t> input_datetime_io; shared_ptr<datetime_io_t> timelog_datetime_io; shared_ptr<datetime_io_t> written_datetime_io; shared_ptr<date_io_t> written_date_io; shared_ptr<datetime_io_t> printed_datetime_io; shared_ptr<date_io_t> printed_date_io; std::deque<shared_ptr<date_io_t> > readers; bool convert_separators_to_slashes = true; date_t parse_date_mask_routine(const char * date_str, date_io_t& io, date_traits_t * traits = NULL) { if (std::strlen(date_str) > 127) { throw_(date_error, _f("Invalid date: %1%") % date_str); } char buf[128]; std::strcpy(buf, date_str); if (convert_separators_to_slashes) { for (char * p = buf; *p; p++) if (*p == '.' || *p == '-') *p = '/'; } date_t when = io.parse(buf); if (! when.is_not_a_date()) { DEBUG("times.parse", "Passed date string: " << date_str); DEBUG("times.parse", "Parsed date string: " << buf); DEBUG("times.parse", "Parsed result is: " << when); DEBUG("times.parse", "Formatted result is: " << io.format(when)); string when_str = io.format(when); const char * p = when_str.c_str(); const char * q = buf; for (; *p && *q; p++, q++) { if (*p != *q && *p == '0') p++; if (! *p || *p != *q) break; } if (*p != '\0' || *q != '\0') throw_(date_error, _f("Invalid date: %1%") % date_str); if (traits) *traits = io.traits; if (! io.traits.has_year) { when = date_t(CURRENT_DATE().year(), when.month(), when.day()); if (when.month() > CURRENT_DATE().month()) when -= gregorian::years(1); } } return when; } date_t parse_date_mask(const char * date_str, date_traits_t * traits = NULL) { foreach (shared_ptr<date_io_t>& reader, readers) { date_t when = parse_date_mask_routine(date_str, *reader.get(), traits); if (! when.is_not_a_date()) return when; } throw_(date_error, _f("Invalid date: %1%") % date_str); return date_t(); } } optional<date_time::weekdays> string_to_day_of_week(const std::string& str) { if (str == _("sun") || str == _("sunday") || str == "0") return gregorian::Sunday; else if (str == _("mon") || str == _("monday") || str == "1") return gregorian::Monday; else if (str == _("tue") || str == _("tuesday") || str == "2") return gregorian::Tuesday; else if (str == _("wed") || str == _("wednesday") || str == "3") return gregorian::Wednesday; else if (str == _("thu") || str == _("thursday") || str == "4") return gregorian::Thursday; else if (str == _("fri") || str == _("friday") || str == "5") return gregorian::Friday; else if (str == _("sat") || str == _("saturday") || str == "6") return gregorian::Saturday; else return none; } optional<date_time::months_of_year> string_to_month_of_year(const std::string& str) { if (str == _("jan") || str == _("january") || str == "0") return gregorian::Jan; else if (str == _("feb") || str == _("february") || str == "1") return gregorian::Feb; else if (str == _("mar") || str == _("march") || str == "2") return gregorian::Mar; else if (str == _("apr") || str == _("april") || str == "3") return gregorian::Apr; else if (str == _("may") || str == _("may") || str == "4") return gregorian::May; else if (str == _("jun") || str == _("june") || str == "5") return gregorian::Jun; else if (str == _("jul") || str == _("july") || str == "6") return gregorian::Jul; else if (str == _("aug") || str == _("august") || str == "7") return gregorian::Aug; else if (str == _("sep") || str == _("september") || str == "8") return gregorian::Sep; else if (str == _("oct") || str == _("october") || str == "9") return gregorian::Oct; else if (str == _("nov") || str == _("november") || str == "10") return gregorian::Nov; else if (str == _("dec") || str == _("december") || str == "11") return gregorian::Dec; else return none; } datetime_t parse_datetime(const char * str) { if (std::strlen(str) > 127) { throw_(date_error, _f("Invalid date: %1%") % str); } char buf[128]; std::strcpy(buf, str); for (char * p = buf; *p; p++) if (*p == '.' || *p == '-') *p = '/'; datetime_t when = input_datetime_io->parse(buf); if (when.is_not_a_date_time()) { when = timelog_datetime_io->parse(buf); if (when.is_not_a_date_time()) { throw_(date_error, _f("Invalid date/time: %1%") % str); } } return when; } date_t parse_date(const char * str) { return parse_date_mask(str); } date_t date_specifier_t::begin() const { year_type the_year = year ? *year : year_type(CURRENT_DATE().year()); month_type the_month = month ? *month : date_t::month_type(1); day_type the_day = day ? *day : date_t::day_type(1); #if !NO_ASSERTS if (day) assert(! wday); else if (wday) assert(! day); #endif // jww (2009-11-16): Handle wday. If a month is set, find the most recent // wday in that month; if the year is set, then in that year. return gregorian::date(static_cast<date_t::year_type>(the_year), static_cast<date_t::month_type>(the_month), static_cast<date_t::day_type>(the_day)); } date_t date_specifier_t::end() const { if (day || wday) return begin() + gregorian::days(1); else if (month) return begin() + gregorian::months(1); else if (year) return begin() + gregorian::years(1); else { assert(false); return date_t(); } } std::ostream& operator<<(std::ostream& out, const date_duration_t& duration) { if (duration.quantum == date_duration_t::DAYS) out << duration.length << " day(s)"; else if (duration.quantum == date_duration_t::WEEKS) out << duration.length << " week(s)"; else if (duration.quantum == date_duration_t::MONTHS) out << duration.length << " month(s)"; else if (duration.quantum == date_duration_t::QUARTERS) out << duration.length << " quarter(s)"; else { assert(duration.quantum == date_duration_t::YEARS); out << duration.length << " year(s)"; } return out; } class date_parser_t { friend void show_period_tokens(std::ostream& out, const string& arg); class lexer_t { friend class date_parser_t; string::const_iterator begin; string::const_iterator end; public: struct token_t { enum kind_t { UNKNOWN, TOK_DATE, TOK_INT, TOK_SLASH, TOK_DASH, TOK_DOT, TOK_A_MONTH, TOK_A_WDAY, TOK_AGO, TOK_HENCE, TOK_SINCE, TOK_UNTIL, TOK_IN, TOK_THIS, TOK_NEXT, TOK_LAST, TOK_EVERY, TOK_TODAY, TOK_TOMORROW, TOK_YESTERDAY, TOK_YEAR, TOK_QUARTER, TOK_MONTH, TOK_WEEK, TOK_DAY, TOK_YEARLY, TOK_QUARTERLY, TOK_BIMONTHLY, TOK_MONTHLY, TOK_BIWEEKLY, TOK_WEEKLY, TOK_DAILY, TOK_YEARS, TOK_QUARTERS, TOK_MONTHS, TOK_WEEKS, TOK_DAYS, END_REACHED } kind; typedef variant<unsigned short, string, date_specifier_t::year_type, date_time::months_of_year, date_time::weekdays, date_specifier_t> content_t; optional<content_t> value; explicit token_t(kind_t _kind = UNKNOWN, const optional<content_t>& _value = content_t(empty_string)) : kind(_kind), value(_value) { TRACE_CTOR(date_parser_t::lexer_t::token_t, ""); } token_t(const token_t& tok) : kind(tok.kind), value(tok.value) { TRACE_CTOR(date_parser_t::lexer_t::token_t, "copy"); } ~token_t() throw() { TRACE_DTOR(date_parser_t::lexer_t::token_t); } token_t& operator=(const token_t& tok) { if (this != &tok) { kind = tok.kind; value = tok.value; } return *this; } operator bool() const { return kind != END_REACHED; } string to_string() const { std::ostringstream out; switch (kind) { case UNKNOWN: out << boost::get<string>(*value); break; case TOK_DATE: return boost::get<date_specifier_t>(*value).to_string(); case TOK_INT: out << boost::get<unsigned short>(*value); break; case TOK_SLASH: return "/"; case TOK_DASH: return "-"; case TOK_DOT: return "."; case TOK_A_MONTH: out << date_specifier_t::month_type (boost::get<date_time::months_of_year>(*value)); break; case TOK_A_WDAY: out << date_specifier_t::day_of_week_type (boost::get<date_time::weekdays>(*value)); break; case TOK_AGO: return "ago"; case TOK_HENCE: return "hence"; case TOK_SINCE: return "since"; case TOK_UNTIL: return "until"; case TOK_IN: return "in"; case TOK_THIS: return "this"; case TOK_NEXT: return "next"; case TOK_LAST: return "last"; case TOK_EVERY: return "every"; case TOK_TODAY: return "today"; case TOK_TOMORROW: return "tomorrow"; case TOK_YESTERDAY: return "yesterday"; case TOK_YEAR: return "year"; case TOK_QUARTER: return "quarter"; case TOK_MONTH: return "month"; case TOK_WEEK: return "week"; case TOK_DAY: return "day"; case TOK_YEARLY: return "yearly"; case TOK_QUARTERLY: return "quarterly"; case TOK_BIMONTHLY: return "bimonthly"; case TOK_MONTHLY: return "monthly"; case TOK_BIWEEKLY: return "biweekly"; case TOK_WEEKLY: return "weekly"; case TOK_DAILY: return "daily"; case TOK_YEARS: return "years"; case TOK_QUARTERS: return "quarters"; case TOK_MONTHS: return "months"; case TOK_WEEKS: return "weeks"; case TOK_DAYS: return "days"; case END_REACHED: return "<EOF>"; } return out.str(); } void dump(std::ostream& out) const { switch (kind) { case UNKNOWN: out << "UNKNOWN"; break; case TOK_DATE: out << "TOK_DATE"; break; case TOK_INT: out << "TOK_INT"; break; case TOK_SLASH: out << "TOK_SLASH"; break; case TOK_DASH: out << "TOK_DASH"; break; case TOK_DOT: out << "TOK_DOT"; break; case TOK_A_MONTH: out << "TOK_A_MONTH"; break; case TOK_A_WDAY: out << "TOK_A_WDAY"; break; case TOK_AGO: out << "TOK_AGO"; break; case TOK_HENCE: out << "TOK_HENCE"; break; case TOK_SINCE: out << "TOK_SINCE"; break; case TOK_UNTIL: out << "TOK_UNTIL"; break; case TOK_IN: out << "TOK_IN"; break; case TOK_THIS: out << "TOK_THIS"; break; case TOK_NEXT: out << "TOK_NEXT"; break; case TOK_LAST: out << "TOK_LAST"; break; case TOK_EVERY: out << "TOK_EVERY"; break; case TOK_TODAY: out << "TOK_TODAY"; break; case TOK_TOMORROW: out << "TOK_TOMORROW"; break; case TOK_YESTERDAY: out << "TOK_YESTERDAY"; break; case TOK_YEAR: out << "TOK_YEAR"; break; case TOK_QUARTER: out << "TOK_QUARTER"; break; case TOK_MONTH: out << "TOK_MONTH"; break; case TOK_WEEK: out << "TOK_WEEK"; break; case TOK_DAY: out << "TOK_DAY"; break; case TOK_YEARLY: out << "TOK_YEARLY"; break; case TOK_QUARTERLY: out << "TOK_QUARTERLY"; break; case TOK_BIMONTHLY: out << "TOK_BIMONTHLY"; break; case TOK_MONTHLY: out << "TOK_MONTHLY"; break; case TOK_BIWEEKLY: out << "TOK_BIWEEKLY"; break; case TOK_WEEKLY: out << "TOK_WEEKLY"; break; case TOK_DAILY: out << "TOK_DAILY"; break; case TOK_YEARS: out << "TOK_YEARS"; break; case TOK_QUARTERS: out << "TOK_QUARTERS"; break; case TOK_MONTHS: out << "TOK_MONTHS"; break; case TOK_WEEKS: out << "TOK_WEEKS"; break; case TOK_DAYS: out << "TOK_DAYS"; break; case END_REACHED: out << "END_REACHED"; break; } } void unexpected(); static void expected(char wanted, char c = '\0'); }; token_t token_cache; lexer_t(string::const_iterator _begin, string::const_iterator _end) : begin(_begin), end(_end) { TRACE_CTOR(date_parser_t::lexer_t, ""); } lexer_t(const lexer_t& other) : begin(other.begin), end(other.end), token_cache(other.token_cache) { TRACE_CTOR(date_parser_t::lexer_t, "copy"); } ~lexer_t() throw() { TRACE_DTOR(date_parser_t::lexer_t); } token_t next_token(); void push_token(token_t tok) { assert(token_cache.kind == token_t::UNKNOWN); token_cache = tok; } token_t peek_token() { if (token_cache.kind == token_t::UNKNOWN) token_cache = next_token(); return token_cache; } }; string arg; lexer_t lexer; public: date_parser_t(const string& _arg) : arg(_arg), lexer(arg.begin(), arg.end()) { TRACE_CTOR(date_parser_t, ""); } date_parser_t(const date_parser_t& parser) : arg(parser.arg), lexer(parser.lexer) { TRACE_CTOR(date_parser_t, "copy"); } ~date_parser_t() throw() { TRACE_DTOR(date_parser_t); } date_interval_t parse(); private: void determine_when(lexer_t::token_t& tok, date_specifier_t& specifier); }; void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok, date_specifier_t& specifier) { date_t today = CURRENT_DATE(); switch (tok.kind) { case lexer_t::token_t::TOK_DATE: specifier = boost::get<date_specifier_t>(*tok.value); break; case lexer_t::token_t::TOK_INT: { unsigned short amount = boost::get<unsigned short>(*tok.value); int8_t adjust = 0; tok = lexer.peek_token(); lexer_t::token_t::kind_t kind = tok.kind; switch (kind) { case lexer_t::token_t::TOK_YEAR: case lexer_t::token_t::TOK_YEARS: case lexer_t::token_t::TOK_QUARTER: case lexer_t::token_t::TOK_QUARTERS: case lexer_t::token_t::TOK_MONTH: case lexer_t::token_t::TOK_MONTHS: case lexer_t::token_t::TOK_WEEK: case lexer_t::token_t::TOK_WEEKS: case lexer_t::token_t::TOK_DAY: case lexer_t::token_t::TOK_DAYS: lexer.next_token(); tok = lexer.next_token(); switch (tok.kind) { case lexer_t::token_t::TOK_AGO: adjust = -1; break; case lexer_t::token_t::TOK_HENCE: adjust = 1; break; default: tok.unexpected(); break; } break; default: break; } date_t when(today); switch (kind) { case lexer_t::token_t::TOK_YEAR: case lexer_t::token_t::TOK_YEARS: when += gregorian::years(amount * adjust); break; case lexer_t::token_t::TOK_QUARTER: case lexer_t::token_t::TOK_QUARTERS: when += gregorian::months(amount * 3 * adjust); break; case lexer_t::token_t::TOK_MONTH: case lexer_t::token_t::TOK_MONTHS: when += gregorian::months(amount * adjust); break; case lexer_t::token_t::TOK_WEEK: case lexer_t::token_t::TOK_WEEKS: when += gregorian::weeks(amount * adjust); break; case lexer_t::token_t::TOK_DAY: case lexer_t::token_t::TOK_DAYS: when += gregorian::days(amount * adjust); break; default: if (amount > 31) { specifier.year = date_specifier_t::year_type(amount); } else { specifier.day = date_specifier_t::day_type(amount); } break; } if (adjust) specifier = date_specifier_t(when); break; } case lexer_t::token_t::TOK_THIS: case lexer_t::token_t::TOK_NEXT: case lexer_t::token_t::TOK_LAST: { int8_t adjust = 0; if (tok.kind == lexer_t::token_t::TOK_NEXT) adjust = 1; else if (tok.kind == lexer_t::token_t::TOK_LAST) adjust = -1; tok = lexer.next_token(); switch (tok.kind) { case lexer_t::token_t::TOK_A_MONTH: { date_t temp(today.year(), boost::get<date_time::months_of_year>(*tok.value), 1); temp += gregorian::years(adjust); specifier = date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()), temp.month()); break; } case lexer_t::token_t::TOK_A_WDAY: { date_t temp = date_duration_t::find_nearest(today, date_duration_t::WEEKS); while (temp.day_of_week() != boost::get<date_time::months_of_year>(*tok.value)) temp += gregorian::days(1); temp += gregorian::days(7 * adjust); specifier = date_specifier_t(temp); break; } case lexer_t::token_t::TOK_YEAR: { date_t temp(today); temp += gregorian::years(adjust); specifier = date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year())); break; } case lexer_t::token_t::TOK_QUARTER: { date_t base = date_duration_t::find_nearest(today, date_duration_t::QUARTERS); date_t temp; if (adjust < 0) { temp = base + gregorian::months(3 * adjust); } else if (adjust == 0) { temp = base + gregorian::months(3); } else if (adjust > 0) { base += gregorian::months(3 * adjust); temp = base + gregorian::months(3 * adjust); } specifier = date_specifier_t(adjust < 0 ? temp : base); break; } case lexer_t::token_t::TOK_WEEK: { date_t base = date_duration_t::find_nearest(today, date_duration_t::WEEKS); date_t temp; if (adjust < 0) { temp = base + gregorian::days(7 * adjust); } else if (adjust == 0) { temp = base + gregorian::days(7); } else if (adjust > 0) { base += gregorian::days(7 * adjust); temp = base + gregorian::days(7 * adjust); } specifier = date_specifier_t(adjust < 0 ? temp : base); break; } case lexer_t::token_t::TOK_DAY: { date_t temp(today); temp += gregorian::days(adjust); specifier = date_specifier_t(temp); break; } default: case lexer_t::token_t::TOK_MONTH: { date_t temp(today); temp += gregorian::months(adjust); specifier = date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()), temp.month()); break; } } break; } case lexer_t::token_t::TOK_A_MONTH: specifier.month = date_specifier_t::month_type (boost::get<date_time::months_of_year>(*tok.value)); tok = lexer.peek_token(); switch (tok.kind) { case lexer_t::token_t::TOK_INT: specifier.year = boost::get<date_specifier_t::year_type>(*tok.value); break; case lexer_t::token_t::END_REACHED: break; default: break; } break; case lexer_t::token_t::TOK_A_WDAY: specifier.wday = date_specifier_t::day_of_week_type (boost::get<date_time::weekdays>(*tok.value)); break; case lexer_t::token_t::TOK_TODAY: specifier = date_specifier_t(today); break; case lexer_t::token_t::TOK_TOMORROW: specifier = date_specifier_t(today + gregorian::days(1)); break; case lexer_t::token_t::TOK_YESTERDAY: specifier = date_specifier_t(today - gregorian::days(1)); break; default: tok.unexpected(); break; } } date_interval_t date_parser_t::parse() { optional<date_specifier_t> since_specifier; optional<date_specifier_t> until_specifier; optional<date_specifier_t> inclusion_specifier; date_interval_t period; date_t today = CURRENT_DATE(); bool end_inclusive = false; for (lexer_t::token_t tok = lexer.next_token(); tok.kind != lexer_t::token_t::END_REACHED; tok = lexer.next_token()) { switch (tok.kind) { case lexer_t::token_t::TOK_DATE: if (! inclusion_specifier) inclusion_specifier = date_specifier_t(); determine_when(tok, *inclusion_specifier); break; case lexer_t::token_t::TOK_INT: if (! inclusion_specifier) inclusion_specifier = date_specifier_t(); determine_when(tok, *inclusion_specifier); break; case lexer_t::token_t::TOK_A_MONTH: if (! inclusion_specifier) inclusion_specifier = date_specifier_t(); determine_when(tok, *inclusion_specifier); break; case lexer_t::token_t::TOK_A_WDAY: if (! inclusion_specifier) inclusion_specifier = date_specifier_t(); determine_when(tok, *inclusion_specifier); break; case lexer_t::token_t::TOK_DASH: if (inclusion_specifier) { since_specifier = inclusion_specifier; until_specifier = date_specifier_t(); inclusion_specifier = none; tok = lexer.next_token(); determine_when(tok, *until_specifier); // The dash operator is special: it has an _inclusive_ end. end_inclusive = true; } else { tok.unexpected(); } break; case lexer_t::token_t::TOK_SINCE: if (since_specifier) { tok.unexpected(); } else { since_specifier = date_specifier_t(); tok = lexer.next_token(); determine_when(tok, *since_specifier); } break; case lexer_t::token_t::TOK_UNTIL: if (until_specifier) { tok.unexpected(); } else { until_specifier = date_specifier_t(); tok = lexer.next_token(); determine_when(tok, *until_specifier); } break; case lexer_t::token_t::TOK_IN: if (inclusion_specifier) { tok.unexpected(); } else { inclusion_specifier = date_specifier_t(); tok = lexer.next_token(); determine_when(tok, *inclusion_specifier); } break; case lexer_t::token_t::TOK_THIS: case lexer_t::token_t::TOK_NEXT: case lexer_t::token_t::TOK_LAST: { int8_t adjust = 0; if (tok.kind == lexer_t::token_t::TOK_NEXT) adjust = 1; else if (tok.kind == lexer_t::token_t::TOK_LAST) adjust = -1; tok = lexer.next_token(); switch (tok.kind) { case lexer_t::token_t::TOK_INT: { unsigned short amount = boost::get<unsigned short>(*tok.value); date_t base(today); date_t end(today); if (! adjust) adjust = 1; tok = lexer.next_token(); switch (tok.kind) { case lexer_t::token_t::TOK_YEARS: base += gregorian::years(amount * adjust); break; case lexer_t::token_t::TOK_QUARTERS: base += gregorian::months(amount * adjust * 3); break; case lexer_t::token_t::TOK_MONTHS: base += gregorian::months(amount * adjust); break; case lexer_t::token_t::TOK_WEEKS: base += gregorian::weeks(amount * adjust); break; case lexer_t::token_t::TOK_DAYS: base += gregorian::days(amount * adjust); break; default: tok.unexpected(); break; } if (adjust >= 0) { date_t temp = base; base = end; end = temp; } since_specifier = date_specifier_t(base); until_specifier = date_specifier_t(end); break; } case lexer_t::token_t::TOK_A_MONTH: { inclusion_specifier = date_specifier_t(); determine_when(tok, *inclusion_specifier); date_t temp(today.year(), *inclusion_specifier->month, 1); temp += gregorian::years(adjust); inclusion_specifier = date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()), temp.month()); break; } case lexer_t::token_t::TOK_A_WDAY: { inclusion_specifier = date_specifier_t(); determine_when(tok, *inclusion_specifier); date_t temp = date_duration_t::find_nearest(today, date_duration_t::WEEKS); while (temp.day_of_week() != inclusion_specifier->wday) temp += gregorian::days(1); temp += gregorian::days(7 * adjust); inclusion_specifier = date_specifier_t(temp); break; } case lexer_t::token_t::TOK_YEAR: { date_t temp(today); temp += gregorian::years(adjust); inclusion_specifier = date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year())); break; } case lexer_t::token_t::TOK_QUARTER: { date_t base = date_duration_t::find_nearest(today, date_duration_t::QUARTERS); date_t temp; if (adjust < 0) { temp = base + gregorian::months(3 * adjust); } else if (adjust == 0) { temp = base + gregorian::months(3); } else if (adjust > 0) { base += gregorian::months(3 * adjust); temp = base + gregorian::months(3 * adjust); } since_specifier = date_specifier_t(adjust < 0 ? temp : base); until_specifier = date_specifier_t(adjust < 0 ? base : temp); break; } case lexer_t::token_t::TOK_WEEK: { date_t base = date_duration_t::find_nearest(today, date_duration_t::WEEKS); date_t temp; if (adjust < 0) { temp = base + gregorian::days(7 * adjust); } else if (adjust == 0) { temp = base + gregorian::days(7); } else if (adjust > 0) { base += gregorian::days(7 * adjust); temp = base + gregorian::days(7 * adjust); } since_specifier = date_specifier_t(adjust < 0 ? temp : base); until_specifier = date_specifier_t(adjust < 0 ? base : temp); break; } case lexer_t::token_t::TOK_DAY: { date_t temp(today); temp += gregorian::days(adjust); inclusion_specifier = date_specifier_t(temp); break; } default: case lexer_t::token_t::TOK_MONTH: { date_t temp(today); temp += gregorian::months(adjust); inclusion_specifier = date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()), temp.month()); break; } } break; } case lexer_t::token_t::TOK_TODAY: inclusion_specifier = date_specifier_t(today); break; case lexer_t::token_t::TOK_TOMORROW: inclusion_specifier = date_specifier_t(today + gregorian::days(1)); break; case lexer_t::token_t::TOK_YESTERDAY: inclusion_specifier = date_specifier_t(today - gregorian::days(1)); break; case lexer_t::token_t::TOK_EVERY: tok = lexer.next_token(); if (tok.kind == lexer_t::token_t::TOK_INT) { int quantity = boost::get<unsigned short>(*tok.value); tok = lexer.next_token(); switch (tok.kind) { case lexer_t::token_t::TOK_YEARS: period.duration = date_duration_t(date_duration_t::YEARS, quantity); break; case lexer_t::token_t::TOK_QUARTERS: period.duration = date_duration_t(date_duration_t::QUARTERS, quantity); break; case lexer_t::token_t::TOK_MONTHS: period.duration = date_duration_t(date_duration_t::MONTHS, quantity); break; case lexer_t::token_t::TOK_WEEKS: period.duration = date_duration_t(date_duration_t::WEEKS, quantity); break; case lexer_t::token_t::TOK_DAYS: period.duration = date_duration_t(date_duration_t::DAYS, quantity); break; default: tok.unexpected(); break; } } else { switch (tok.kind) { case lexer_t::token_t::TOK_YEAR: period.duration = date_duration_t(date_duration_t::YEARS, 1); break; case lexer_t::token_t::TOK_QUARTER: period.duration = date_duration_t(date_duration_t::QUARTERS, 1); break; case lexer_t::token_t::TOK_MONTH: period.duration = date_duration_t(date_duration_t::MONTHS, 1); break; case lexer_t::token_t::TOK_WEEK: period.duration = date_duration_t(date_duration_t::WEEKS, 1); break; case lexer_t::token_t::TOK_DAY: period.duration = date_duration_t(date_duration_t::DAYS, 1); break; default: tok.unexpected(); break; } } break; case lexer_t::token_t::TOK_YEARLY: period.duration = date_duration_t(date_duration_t::YEARS, 1); break; case lexer_t::token_t::TOK_QUARTERLY: period.duration = date_duration_t(date_duration_t::QUARTERS, 1); break; case lexer_t::token_t::TOK_BIMONTHLY: period.duration = date_duration_t(date_duration_t::MONTHS, 2); break; case lexer_t::token_t::TOK_MONTHLY: period.duration = date_duration_t(date_duration_t::MONTHS, 1); break; case lexer_t::token_t::TOK_BIWEEKLY: period.duration = date_duration_t(date_duration_t::WEEKS, 2); break; case lexer_t::token_t::TOK_WEEKLY: period.duration = date_duration_t(date_duration_t::WEEKS, 1); break; case lexer_t::token_t::TOK_DAILY: period.duration = date_duration_t(date_duration_t::DAYS, 1); break; default: tok.unexpected(); break; } } #if 0 if (! period.duration && inclusion_specifier) period.duration = inclusion_specifier->implied_duration(); #endif if (since_specifier || until_specifier) { date_range_t range(since_specifier, until_specifier); range.end_inclusive = end_inclusive; period.range = date_specifier_or_range_t(range); } else if (inclusion_specifier) { period.range = date_specifier_or_range_t(*inclusion_specifier); } else { /* otherwise, it's something like "monthly", with no date reference */ } return period; } void date_interval_t::parse(const string& str) { date_parser_t parser(str); *this = parser.parse(); } void date_interval_t::resolve_end() { if (start && ! end_of_duration) { end_of_duration = duration->add(*start); DEBUG("times.interval", "stabilize: end_of_duration = " << *end_of_duration); } if (finish && *end_of_duration > *finish) { end_of_duration = finish; DEBUG("times.interval", "stabilize: end_of_duration reset to end: " << *end_of_duration); } if (start && ! next) { next = end_of_duration; DEBUG("times.interval", "stabilize: next set to: " << *next); } } date_t date_duration_t::find_nearest(const date_t& date, skip_quantum_t skip) { date_t result; switch (skip) { case date_duration_t::YEARS: result = date_t(date.year(), gregorian::Jan, 1); break; case date_duration_t::QUARTERS: result = date_t(date.year(), date.month(), 1); while (result.month() != gregorian::Jan && result.month() != gregorian::Apr && result.month() != gregorian::Jul && result.month() != gregorian::Oct) result -= gregorian::months(1); break; case date_duration_t::MONTHS: result = date_t(date.year(), date.month(), 1); break; case date_duration_t::WEEKS: result = date; while (result.day_of_week() != start_of_week) result -= gregorian::days(1); break; case date_duration_t::DAYS: result = date; break; } return result; } void date_interval_t::stabilize(const optional<date_t>& date) { #if DEBUG_ON if (date) DEBUG("times.interval", "stabilize: with date = " << *date); #endif if (date && ! aligned) { DEBUG("times.interval", "stabilize: date passed, but not aligned"); if (duration) { DEBUG("times.interval", "stabilize: aligning with a duration: " << *duration); // The interval object has not been seeded with a start date yet, so // find the nearest period before or on date which fits, if possible. // // Find an efficient starting point for the upcoming while loop. We // want a date early enough that the range will be correct, but late // enough that we don't spend hundreds of thousands of loops skipping // through time. optional<date_t> initial_start = start ? start : begin(); optional<date_t> initial_finish = finish ? finish : end(); #if DEBUG_ON if (initial_start) DEBUG("times.interval", "stabilize: initial_start = " << *initial_start); if (initial_finish) DEBUG("times.interval", "stabilize: initial_finish = " << *initial_finish); #endif date_t when = start ? *start : *date; switch (duration->quantum) { case date_duration_t::MONTHS: case date_duration_t::QUARTERS: case date_duration_t::YEARS: // These start on most recent period start quantum before when DEBUG("times.interval", "stabilize: monthly, quarterly or yearly duration"); start = date_duration_t::find_nearest(when, duration->quantum); break; case date_duration_t::WEEKS: // Weeks start on the beginning of week prior to 400 remainder period length // Either the first quanta of the period or the last quanta of the period seems more sensible // implies period is never less than 400 days not too unreasonable DEBUG("times.interval", "stabilize: weekly duration"); { int period = duration->length * 7; start = date_duration_t::find_nearest( when - gregorian::days(period + 400 % period), duration->quantum); } break; default: // multiples of days have a quanta of 1 day so should not have the start date adjusted to a quanta DEBUG("times.interval", "stabilize: daily duration - stable by definition"); start = when; break; } DEBUG("times.interval", "stabilize: beginning start date = " << *start); while (*start < *date) { date_interval_t next_interval(*this); ++next_interval; if (next_interval.start && *next_interval.start <= *date) { *this = next_interval; } else { end_of_duration = none; next = none; break; } } DEBUG("times.interval", "stabilize: proposed start date = " << *start); if (initial_start && (! start || *start < *initial_start)) { // Using the discovered start, find the end of the period resolve_end(); start = initial_start; DEBUG("times.interval", "stabilize: start reset to initial start"); } if (initial_finish && (! finish || *finish > *initial_finish)) { finish = initial_finish; DEBUG("times.interval", "stabilize: finish reset to initial finish"); } #if DEBUG_ON if (start) DEBUG("times.interval", "stabilize: final start = " << *start); if (finish) DEBUG("times.interval", "stabilize: final finish = " << *finish); #endif } else if (range) { start = range->begin(); finish = range->end(); } aligned = true; } // If there is no duration, then if we've reached here the date falls // between start and finish. if (! duration) { DEBUG("times.interval", "stabilize: there was no duration given"); if (! start && ! finish) throw_(date_error, _("Invalid date interval: neither start, nor finish, nor duration")); } else { resolve_end(); } } bool date_interval_t::find_period(const date_t& date, const bool allow_shift) { stabilize(date); if (finish && date > *finish) { DEBUG("times.interval", "false: date [" << date << "] > finish [" << *finish << "]"); return false; } if (! start) { throw_(std::runtime_error, _("Date interval is improperly initialized")); } else if (date < *start) { DEBUG("times.interval", "false: date [" << date << "] < start [" << *start << "]"); return false; } if (end_of_duration) { if (date < *end_of_duration) { DEBUG("times.interval", "true: date [" << date << "] < end_of_duration [" << *end_of_duration << "]"); return true; } } else { DEBUG("times.interval", "false: there is no end_of_duration"); return false; } // If we've reached here, it means the date does not fall into the current // interval, so we must seek another interval that does match -- unless we // pass by date in so doing, which means we shouldn't alter the current // period of the interval at all. date_t scan = *start; date_t end_of_scan = *end_of_duration; DEBUG("times.interval", "date = " << date); DEBUG("times.interval", "scan = " << scan); DEBUG("times.interval", "end_of_scan = " << end_of_scan); #if DEBUG_ON if (finish) DEBUG("times.interval", "finish = " << *finish); else DEBUG("times.interval", "finish is not set"); #endif while (date >= scan && (! finish || scan < *finish)) { if (date < end_of_scan) { start = scan; end_of_duration = end_of_scan; next = none; DEBUG("times.interval", "true: start = " << *start); DEBUG("times.interval", "true: end_of_duration = " << *end_of_duration); resolve_end(); return true; } else if (! allow_shift) { break; } scan = duration->add(scan); end_of_scan = duration->add(scan); DEBUG("times.interval", "scan = " << scan); DEBUG("times.interval", "end_of_scan = " << end_of_scan); } DEBUG("times.interval", "false: failed scan"); return false; } date_interval_t& date_interval_t::operator++() { if (! start) throw_(date_error, _("Cannot increment an unstarted date interval")); stabilize(); if (! duration) throw_(date_error, _("Cannot increment a date interval without a duration")); assert(next); if (finish && *next >= *finish) { start = none; } else { start = *next; end_of_duration = duration->add(*start); } next = none; resolve_end(); return *this; } void date_interval_t::dump(std::ostream& out) { out << _("--- Before stabilization ---") << std::endl; if (range) out << _(" range: ") << range->to_string() << std::endl; if (start) out << _(" start: ") << format_date(*start) << std::endl; if (finish) out << _(" finish: ") << format_date(*finish) << std::endl; if (duration) out << _("duration: ") << duration->to_string() << std::endl; optional<date_t> when(begin()); if (! when) when = CURRENT_DATE(); stabilize(when); out << std::endl << _("--- After stabilization ---") << std::endl; if (range) out << _(" range: ") << range->to_string() << std::endl; if (start) out << _(" start: ") << format_date(*start) << std::endl; if (finish) out << _(" finish: ") << format_date(*finish) << std::endl; if (duration) out << _("duration: ") << duration->to_string() << std::endl; out << std::endl << _("--- Sample dates in range (max. 20) ---") << std::endl; date_t last_date; for (int i = 0; i < 20 && *this; ++i, ++*this) { out << std::right; out.width(2); if (! last_date.is_not_a_date() && last_date == *start) break; out << (i + 1) << ": " << format_date(*start); if (duration) out << " -- " << format_date(*inclusive_end()); out << std::endl; if (! duration) break; last_date = *start; } } date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token() { if (token_cache.kind != token_t::UNKNOWN) { token_t tok = token_cache; token_cache = token_t(); return tok; } while (begin != end && std::isspace(*begin)) begin++; if (begin == end) return token_t(token_t::END_REACHED); switch (*begin) { case '/': ++begin; return token_t(token_t::TOK_SLASH); case '-': ++begin; return token_t(token_t::TOK_DASH); case '.': ++begin; return token_t(token_t::TOK_DOT); default: break; } string::const_iterator start = begin; // If the first character is a digit, try parsing the whole argument as a // date using the typical date formats. This allows not only dates like // "2009/08/01", but also dates that fit the user's --input-date-format, // assuming their format fits in one argument and begins with a digit. if (std::isdigit(*begin)) { string::const_iterator i = begin; for (i = begin; i != end && ! std::isspace(*i); i++) {} assert(i != begin); string possible_date(start, i); try { date_traits_t traits; date_t when = parse_date_mask(possible_date.c_str(), &traits); if (! when.is_not_a_date()) { begin = i; return token_t(token_t::TOK_DATE, token_t::content_t(date_specifier_t(when, traits))); } } catch (date_error&) { if (contains(possible_date, "/") || contains(possible_date, "-") || contains(possible_date, ".")) throw; } } start = begin; string term; bool alnum = std::isalnum(*begin); for (; (begin != end && ! std::isspace(*begin) && ((alnum && static_cast<bool>(std::isalnum(*begin))) || (! alnum && ! static_cast<bool>(std::isalnum(*begin))))); begin++) term.push_back(*begin); if (! term.empty()) { if (std::isdigit(term[0])) { return token_t(token_t::TOK_INT, token_t::content_t(lexical_cast<unsigned short>(term))); } else if (std::isalpha(term[0])) { to_lower(term); if (optional<date_time::months_of_year> month = string_to_month_of_year(term)) { return token_t(token_t::TOK_A_MONTH, token_t::content_t(*month)); } else if (optional<date_time::weekdays> wday = string_to_day_of_week(term)) { return token_t(token_t::TOK_A_WDAY, token_t::content_t(*wday)); } else if (term == _("ago")) return token_t(token_t::TOK_AGO); else if (term == _("hence")) return token_t(token_t::TOK_HENCE); else if (term == _("from") || term == _("since")) return token_t(token_t::TOK_SINCE); else if (term == _("to") || term == _("until")) return token_t(token_t::TOK_UNTIL); else if (term == _("in")) return token_t(token_t::TOK_IN); else if (term == _("this")) return token_t(token_t::TOK_THIS); else if (term == _("next")) return token_t(token_t::TOK_NEXT); else if (term == _("last")) return token_t(token_t::TOK_LAST); else if (term == _("every")) return token_t(token_t::TOK_EVERY); else if (term == _("today")) return token_t(token_t::TOK_TODAY); else if (term == _("tomorrow")) return token_t(token_t::TOK_TOMORROW); else if (term == _("yesterday")) return token_t(token_t::TOK_YESTERDAY); else if (term == _("year")) return token_t(token_t::TOK_YEAR); else if (term == _("quarter")) return token_t(token_t::TOK_QUARTER); else if (term == _("month")) return token_t(token_t::TOK_MONTH); else if (term == _("week")) return token_t(token_t::TOK_WEEK); else if (term == _("day")) return token_t(token_t::TOK_DAY); else if (term == _("yearly")) return token_t(token_t::TOK_YEARLY); else if (term == _("quarterly")) return token_t(token_t::TOK_QUARTERLY); else if (term == _("bimonthly")) return token_t(token_t::TOK_BIMONTHLY); else if (term == _("monthly")) return token_t(token_t::TOK_MONTHLY); else if (term == _("biweekly")) return token_t(token_t::TOK_BIWEEKLY); else if (term == _("weekly")) return token_t(token_t::TOK_WEEKLY); else if (term == _("daily")) return token_t(token_t::TOK_DAILY); else if (term == _("years")) return token_t(token_t::TOK_YEARS); else if (term == _("quarters")) return token_t(token_t::TOK_QUARTERS); else if (term == _("months")) return token_t(token_t::TOK_MONTHS); else if (term == _("weeks")) return token_t(token_t::TOK_WEEKS); else if (term == _("days")) return token_t(token_t::TOK_DAYS); } else { token_t::expected('\0', term[0]); begin = ++start; } } else { token_t::expected('\0', *begin); } return token_t(token_t::UNKNOWN, token_t::content_t(term)); } void date_parser_t::lexer_t::token_t::unexpected() { switch (kind) { case END_REACHED: kind = UNKNOWN; throw_(date_error, _("Unexpected end of expression")); default: { string desc = to_string(); kind = UNKNOWN; throw_(date_error, _f("Unexpected date period token '%1%'") % desc); } } } void date_parser_t::lexer_t::token_t::expected(char wanted, char c) { if (wanted == '\0') throw_(date_error, _f("Invalid char '%1%'") % c); else throw_(date_error, _f("Invalid char '%1%' (wanted '%2%')") % c % wanted); } namespace { typedef std::map<std::string, datetime_io_t *> datetime_io_map; typedef std::map<std::string, date_io_t *> date_io_map; datetime_io_map temp_datetime_io; date_io_map temp_date_io; } std::string format_datetime(const datetime_t& when, const format_type_t format_type, const optional<const char *>& format) { if (format_type == FMT_WRITTEN) { return written_datetime_io->format(when); } else if (format_type == FMT_CUSTOM && format) { datetime_io_map::iterator i = temp_datetime_io.find(*format); if (i != temp_datetime_io.end()) { return (*i).second->format(when); } else { datetime_io_t * formatter = new datetime_io_t(*format, false); temp_datetime_io.insert(datetime_io_map::value_type(*format, formatter)); return formatter->format(when); } } else if (format_type == FMT_PRINTED) { return printed_datetime_io->format(when); } else { assert(false); return empty_string; } } std::string format_date(const date_t& when, const format_type_t format_type, const optional<const char *>& format) { if (format_type == FMT_WRITTEN) { return written_date_io->format(when); } else if (format_type == FMT_CUSTOM && format) { date_io_map::iterator i = temp_date_io.find(*format); if (i != temp_date_io.end()) { return (*i).second->format(when); } else { date_io_t * formatter = new date_io_t(*format, false); temp_date_io.insert(date_io_map::value_type(*format, formatter)); return formatter->format(when); } } else if (format_type == FMT_PRINTED) { return printed_date_io->format(when); } else { assert(false); return empty_string; } } namespace { bool is_initialized = false; } void set_datetime_format(const char * format) { written_datetime_io->set_format(format); printed_datetime_io->set_format(format); } void set_date_format(const char * format) { written_date_io->set_format(format); printed_date_io->set_format(format); } void set_input_date_format(const char * format) { readers.push_front(shared_ptr<date_io_t>(new date_io_t(format, true))); convert_separators_to_slashes = false; } void times_initialize() { if (! is_initialized) { input_datetime_io.reset(new datetime_io_t("%Y/%m/%d %H:%M:%S", true)); timelog_datetime_io.reset(new datetime_io_t("%m/%d/%Y %H:%M:%S", true)); written_datetime_io.reset(new datetime_io_t("%Y/%m/%d %H:%M:%S", false)); written_date_io.reset(new date_io_t("%Y/%m/%d", false)); printed_datetime_io.reset(new datetime_io_t("%y-%b-%d %H:%M:%S", false)); printed_date_io.reset(new date_io_t("%y-%b-%d", false)); readers.push_back(shared_ptr<date_io_t>(new date_io_t("%m/%d", true))); readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y/%m/%d", true))); readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y/%m", true))); readers.push_back(shared_ptr<date_io_t>(new date_io_t("%y/%m/%d", true))); readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y-%m-%d", true))); is_initialized = true; } } void times_shutdown() { if (is_initialized) { input_datetime_io.reset(); timelog_datetime_io.reset(); written_datetime_io.reset(); written_date_io.reset(); printed_datetime_io.reset(); printed_date_io.reset(); readers.clear(); foreach (datetime_io_map::value_type& pair, temp_datetime_io) checked_delete(pair.second); temp_datetime_io.clear(); foreach (date_io_map::value_type& pair, temp_date_io) checked_delete(pair.second); temp_date_io.clear(); is_initialized = false; } } void show_period_tokens(std::ostream& out, const string& arg) { date_parser_t::lexer_t lexer(arg.begin(), arg.end()); out << _("--- Period expression tokens ---") << std::endl; date_parser_t::lexer_t::token_t token; do { token = lexer.next_token(); token.dump(out); out << ": " << token.to_string() << std::endl; } while (token.kind != date_parser_t::lexer_t::token_t::END_REACHED); } } // namespace ledger ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/times.h����������������������������������������������������������������������������0000664�0000000�0000000�00000036237�14411236400�0015050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup util */ /** * @file times.h * @author John Wiegley * * @ingroup util * * @brief datetime_t and date_t objects */ #ifndef INCLUDED_TIMES_H #define INCLUDED_TIMES_H #include "utils.h" namespace ledger { DECLARE_EXCEPTION(datetime_error, std::runtime_error); DECLARE_EXCEPTION(date_error, std::runtime_error); typedef boost::posix_time::ptime datetime_t; typedef datetime_t::time_duration_type time_duration_t; inline bool is_valid(const datetime_t& moment) { return ! moment.is_not_a_date_time(); } typedef boost::gregorian::date date_t; typedef boost::gregorian::date_iterator date_iterator_t; inline bool is_valid(const date_t& moment) { return ! moment.is_not_a_date(); } extern optional<datetime_t> epoch; #ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK #define TRUE_CURRENT_TIME() (boost::posix_time::microsec_clock::local_time()) #define CURRENT_TIME() (epoch ? *epoch : TRUE_CURRENT_TIME()) #else #define TRUE_CURRENT_TIME() (boost::posix_time::second_clock::local_time()) #define CURRENT_TIME() (epoch ? *epoch : TRUE_CURRENT_TIME()) #endif #define CURRENT_DATE() \ (epoch ? epoch->date() : boost::gregorian::day_clock::local_day()) extern date_time::weekdays start_of_week; optional<date_time::weekdays> string_to_day_of_week(const std::string& str); optional<date_time::months_of_year> string_to_month_of_year(const std::string& str); datetime_t parse_datetime(const char * str); inline datetime_t parse_datetime(const std::string& str) { return parse_datetime(str.c_str()); } date_t parse_date(const char * str); inline date_t parse_date(const std::string& str) { return parse_date(str.c_str()); } enum format_type_t { FMT_WRITTEN, FMT_PRINTED, FMT_CUSTOM }; std::string format_datetime(const datetime_t& when, const format_type_t format_type = FMT_PRINTED, const optional<const char *>& format = none); void set_datetime_format(const char * format); std::string format_date(const date_t& when, const format_type_t format_type = FMT_PRINTED, const optional<const char *>& format = none); void set_date_format(const char * format); void set_input_date_format(const char * format); inline void put_datetime(property_tree::ptree& pt, const datetime_t& when) { pt.put_value(format_datetime(when, FMT_WRITTEN)); } inline void put_date(property_tree::ptree& pt, const date_t& when) { pt.put_value(format_date(when, FMT_WRITTEN)); } struct date_traits_t { bool has_year; bool has_month; bool has_day; date_traits_t(bool _has_year = false, bool _has_month = false, bool _has_day = false) : has_year(_has_year), has_month(_has_month), has_day(_has_day) { TRACE_CTOR(date_traits_t, "bool, bool, bool"); } date_traits_t(const date_traits_t& traits) : has_year(traits.has_year), has_month(traits.has_month), has_day(traits.has_day) { TRACE_CTOR(date_traits_t, "copy"); } ~date_traits_t() throw() { TRACE_DTOR(date_traits_t); } date_traits_t& operator=(const date_traits_t& traits) { has_year = traits.has_year; has_month = traits.has_month; has_day = traits.has_day; return *this; } bool operator==(const date_traits_t& traits) const { return (has_year == traits.has_year && has_month == traits.has_month && has_day == traits.has_day); } }; struct date_duration_t { enum skip_quantum_t { DAYS, WEEKS, MONTHS, QUARTERS, YEARS } quantum; int length; date_duration_t() : quantum(DAYS), length(0) { TRACE_CTOR(date_duration_t, ""); } date_duration_t(skip_quantum_t _quantum, int _length) : quantum(_quantum), length(_length) { TRACE_CTOR(date_duration_t, "skip_quantum_t, int"); } date_duration_t(const date_duration_t& dur) : quantum(dur.quantum), length(dur.length) { TRACE_CTOR(date_duration_t, "copy"); } ~date_duration_t() throw() { TRACE_DTOR(date_duration_t); } date_t add(const date_t& date) const { switch (quantum) { case DAYS: return date + gregorian::days(length); case WEEKS: return date + gregorian::weeks(length); case MONTHS: return date + gregorian::months(length); case QUARTERS: return date + gregorian::months(length * 3); case YEARS: return date + gregorian::years(length); } #if !defined(__clang__) return date_t(); #endif } date_t subtract(const date_t& date) const { switch (quantum) { case DAYS: return date - gregorian::days(length); case WEEKS: return date - gregorian::weeks(length); case MONTHS: return date - gregorian::months(length); case QUARTERS: return date - gregorian::months(length * 3); case YEARS: return date - gregorian::years(length); } #if !defined(__clang__) return date_t(); #endif } string to_string() const { std::ostringstream out; out << length << ' '; switch (quantum) { case DAYS: out << "day"; break; case WEEKS: out << "week"; break; case MONTHS: out << "month"; break; case QUARTERS: out << "quarter"; break; case YEARS: out << "year"; break; } if (length > 1) out << 's'; return out.str(); } static date_t find_nearest(const date_t& date, skip_quantum_t skip); }; class date_specifier_t { friend class date_parser_t; public: #if 0 typedef date_t::year_type year_type; #else typedef unsigned short year_type; #endif typedef date_t::month_type month_type; typedef date_t::day_type day_type; typedef date_t::day_of_week_type day_of_week_type; protected: optional<year_type> year; optional<month_type> month; optional<day_type> day; optional<day_of_week_type> wday; public: date_specifier_t(const optional<year_type>& _year = none, const optional<month_type>& _month = none, const optional<day_type>& _day = none, const optional<day_of_week_type>& _wday = none) : year(_year), month(_month), day(_day), wday(_wday) { TRACE_CTOR(date_specifier_t, "year_type, month_type, day_type, day_of_week_type"); } date_specifier_t(const date_t& date, const optional<date_traits_t>& traits = none) { if (! traits || traits->has_year) year = date.year(); if (! traits || traits->has_month) month = date.month(); if (! traits || traits->has_day) day = date.day(); TRACE_CTOR(date_specifier_t, "date_t, date_traits_t"); } date_specifier_t(const date_specifier_t& other) : year(other.year), month(other.month), day(other.day), wday(other.wday) { TRACE_CTOR(date_specifier_t, "copy"); } ~date_specifier_t() throw() { TRACE_DTOR(date_specifier_t); } date_t begin() const; date_t end() const; bool is_within(const date_t& date) const { return date >= begin() && date < end(); } optional<date_duration_t> implied_duration() const { if (day || wday) return date_duration_t(date_duration_t::DAYS, 1); else if (month) return date_duration_t(date_duration_t::MONTHS, 1); else if (year) return date_duration_t(date_duration_t::YEARS, 1); else return none; } string to_string() const { std::ostringstream out; if (year) out << " year " << *year; if (month) out << " month " << *month; if (day) out << " day " << *day; if (wday) out << " wday " << *wday; return out.str(); } }; class date_range_t { friend class date_parser_t; optional<date_specifier_t> range_begin; optional<date_specifier_t> range_end; bool end_inclusive; public: date_range_t(const optional<date_specifier_t>& _range_begin = none, const optional<date_specifier_t>& _range_end = none) : range_begin(_range_begin), range_end(_range_end), end_inclusive(false) { TRACE_CTOR(date_range_t, "date_specifier_t, date_specifier_t"); } date_range_t(const date_range_t& other) : range_begin(other.range_begin), range_end(other.range_end), end_inclusive(other.end_inclusive) { TRACE_CTOR(date_range_t, "date_range_t"); } ~date_range_t() throw() { TRACE_DTOR(date_range_t); } optional<date_t> begin() const { if (range_begin) return range_begin->begin(); else return none; } optional<date_t> end() const { if (range_end) { if (end_inclusive) return range_end->end(); else return range_end->begin(); } else { return none; } } bool is_within(const date_t& date) const { optional<date_t> b = begin(); optional<date_t> e = end(); bool after_begin = b ? date >= *b : true; bool before_end = e ? date < *e : true; return after_begin && before_end; } string to_string() const { std::ostringstream out; if (range_begin) out << "from" << range_begin->to_string(); if (range_end) out << " to" << range_end->to_string(); return out.str(); } }; class date_specifier_or_range_t { typedef variant<int, date_specifier_t, date_range_t> value_type; value_type specifier_or_range; public: date_specifier_or_range_t() { TRACE_CTOR(date_specifier_or_range_t, ""); } date_specifier_or_range_t(const date_specifier_or_range_t& other) : specifier_or_range(other.specifier_or_range) { TRACE_CTOR(date_specifier_or_range_t, "copy"); } date_specifier_or_range_t(const date_specifier_t& specifier) : specifier_or_range(specifier) { TRACE_CTOR(date_specifier_or_range_t, "date_specifier_t"); } date_specifier_or_range_t(const date_range_t& range) : specifier_or_range(range) { TRACE_CTOR(date_specifier_or_range_t, "date_range_t"); } ~date_specifier_or_range_t() throw() { TRACE_DTOR(date_specifier_or_range_t); } optional<date_t> begin() const { if (specifier_or_range.type() == typeid(date_specifier_t)) return boost::get<date_specifier_t>(specifier_or_range).begin(); else if (specifier_or_range.type() == typeid(date_range_t)) return boost::get<date_range_t>(specifier_or_range).begin(); else return none; } optional<date_t> end() const { if (specifier_or_range.type() == typeid(date_specifier_t)) return boost::get<date_specifier_t>(specifier_or_range).end(); else if (specifier_or_range.type() == typeid(date_range_t)) return boost::get<date_range_t>(specifier_or_range).end(); else return none; } string to_string() const { std::ostringstream out; if (specifier_or_range.type() == typeid(date_specifier_t)) out << "in" << boost::get<date_specifier_t>(specifier_or_range).to_string(); else if (specifier_or_range.type() == typeid(date_range_t)) out << boost::get<date_range_t>(specifier_or_range).to_string(); return out.str(); } }; class date_interval_t : public equality_comparable<date_interval_t> { public: static date_t add_duration(const date_t& date, const date_duration_t& duration); static date_t subtract_duration(const date_t& date, const date_duration_t& duration); optional<date_specifier_or_range_t> range; optional<date_t> start; // the real start, after adjustment optional<date_t> finish; // the real end, likewise bool aligned; optional<date_t> next; optional<date_duration_t> duration; optional<date_t> end_of_duration; explicit date_interval_t() : aligned(false) { TRACE_CTOR(date_interval_t, ""); } date_interval_t(const string& str) : aligned(false) { parse(str); TRACE_CTOR(date_interval_t, "const string&"); } date_interval_t(const date_interval_t& other) : range(other.range), start(other.start), finish(other.finish), aligned(other.aligned), next(other.next), duration(other.duration), end_of_duration(other.end_of_duration) { TRACE_CTOR(date_interval_t, "copy"); } ~date_interval_t() throw() { TRACE_DTOR(date_interval_t); } bool operator==(const date_interval_t& other) const { return (start == other.start && (! start || *start == *other.start)); } bool operator<(const date_interval_t& other) const { return (start == other.start && (! start || *start < *other.start)); } operator bool() const { return is_valid(); } optional<date_t> begin() const { return start ? start : (range ? range->begin() : none); } optional<date_t> end() const { return finish ? finish : (range ? range->end() : none); } void parse(const string& str); void resolve_end(); void stabilize(const optional<date_t>& date = none); bool is_valid() const { return static_cast<bool>(start); } /** Find the current or next period containing date. Returns false if no such period can be found. If allow_shift is true, the default, then the interval may be shifted in time to find the period. */ bool find_period(const date_t& date = CURRENT_DATE(), const bool allow_shift = true); bool within_period(const date_t& date = CURRENT_DATE()) { return find_period(date, false); } optional<date_t> inclusive_end() const { if (end_of_duration) return *end_of_duration - gregorian::days(1); else return none; } date_interval_t& operator++(); void dump(std::ostream& out); }; void times_initialize(); void times_shutdown(); void show_period_tokens(std::ostream& out, const string& arg); std::ostream& operator<<(std::ostream& out, const date_duration_t& duration); } // namespace ledger #endif // INCLUDED_TIMES_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/token.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000034200�14411236400�0015171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "token.h" #include "parser.h" namespace ledger { int expr_t::token_t::parse_reserved_word(std::istream& in) { int c = in.peek(); if (c == 'a' || c == 'd' || c == 'e' || c == 'f' || c == 'i' || c == 'o' || c == 'n' || c == 't') { length = 0; char buf[6]; READ_INTO_(in, buf, 5, c, length, std::isalpha(c)); switch (buf[0]) { case 'a': if (std::strcmp(buf, "and") == 0) { symbol[0] = '&'; symbol[1] = '\0'; kind = KW_AND; return 1; } break; case 'd': if (std::strcmp(buf, "div") == 0) { symbol[0] = '/'; symbol[1] = '\0'; kind = KW_DIV; return 1; } break; case 'e': if (std::strcmp(buf, "else") == 0) { std::strcpy(symbol, "else"); kind = KW_ELSE; return 1; } break; case 'f': if (std::strcmp(buf, "false") == 0) { std::strcpy(symbol, "false"); kind = VALUE; value = false; return 1; } break; case 'i': if (std::strcmp(buf, "if") == 0) { symbol[0] = 'i'; symbol[1] = 'f'; symbol[2] = '\0'; kind = KW_IF; return 1; } break; case 'o': if (std::strcmp(buf, "or") == 0) { symbol[0] = '|'; symbol[1] = '\0'; kind = KW_OR; return 1; } break; case 'n': if (std::strcmp(buf, "not") == 0) { symbol[0] = '!'; symbol[1] = '\0'; kind = EXCLAM; return 1; } break; case 't': if (std::strcmp(buf, "true") == 0) { std::strcpy(symbol, "true"); kind = VALUE; value = true; return 1; } break; } return 0; } return -1; } void expr_t::token_t::parse_ident(std::istream& in) { kind = IDENT; length = 0; int c; char buf[256]; READ_INTO_(in, buf, 255, c, length, std::isalpha(c) || c == '_'); value.set_string(buf); } void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags) { if (in.eof()) { kind = TOK_EOF; return; } if (! in.good()) throw_(parse_error, _("Input stream no longer valid")); int c = peek_next_nonws(in); if (in.eof()) { kind = TOK_EOF; return; } if (! in.good()) throw_(parse_error, _("Input stream no longer valid")); symbol[0] = c; symbol[1] = '\0'; length = 1; switch (c) { case '&': in.get(); c = in.peek(); if (c == '&') { in.get(); kind = KW_AND; length = 2; break; } kind = KW_AND; break; case '|': in.get(); c = in.peek(); if (c == '|') { in.get(); kind = KW_OR; length = 2; break; } kind = KW_OR; break; case '(': in.get(); kind = LPAREN; break; case ')': in.get(); kind = RPAREN; break; case '[': { in.get(); char buf[256]; READ_INTO_(in, buf, 255, c, length, c != ']'); if (c != ']') expected(']', c); in.get(); length++; date_interval_t timespan(buf); optional<date_t> begin = timespan.begin(); if (! begin) throw_(parse_error, _("Date specifier does not refer to a starting date")); kind = VALUE; value = *begin; break; } case '\'': case '"': { char delim; in.get(delim); char buf[4096]; READ_INTO_(in, buf, 4095, c, length, c != delim); if (c != delim) expected(delim, c); in.get(); length++; kind = VALUE; value.set_string(buf); break; } case '{': { in.get(); amount_t temp; temp.parse(in, PARSE_NO_MIGRATE); c = in.get(); if (c != '}') expected('}', c); length++; kind = VALUE; value = temp; break; } case '!': in.get(); c = in.peek(); if (c == '=') { in.get(); symbol[1] = c; symbol[2] = '\0'; kind = NEQUAL; length = 2; break; } else if (c == '~') { in.get(); symbol[1] = c; symbol[2] = '\0'; kind = NMATCH; length = 2; break; } kind = EXCLAM; break; case '-': in.get(); c = in.peek(); if (c == '>') { in.get(); symbol[1] = c; symbol[2] = '\0'; kind = ARROW; length = 2; break; } kind = MINUS; break; case '+': in.get(); kind = PLUS; break; case '*': in.get(); kind = STAR; break; case '?': in.get(); kind = QUERY; break; case ':': in.get(); kind = COLON; break; case '/': { in.get(); if (pflags.has_flags(PARSE_OP_CONTEXT)) { // operator context kind = SLASH; } else { // terminal context // Read in the regexp char buf[4096]; READ_INTO_(in, buf, 4095, c, length, c != '/'); if (c != '/') expected('/', c); in.get(); length++; kind = VALUE; value.set_mask(buf); } break; } case '=': in.get(); c = in.peek(); if (c == '~') { in.get(); symbol[1] = c; symbol[2] = '\0'; kind = MATCH; length = 2; break; } else if (c == '=') { in.get(); symbol[1] = c; symbol[2] = '\0'; kind = EQUAL; length = 2; break; } kind = ASSIGN; break; case '<': in.get(); if (in.peek() == '=') { symbol[1] = in.get(); symbol[2] = '\0'; kind = LESSEQ; length = 2; break; } kind = LESS; break; case '>': in.get(); if (in.peek() == '=') { symbol[1] = in.get(); symbol[2] = '\0'; kind = GREATEREQ; length = 2; break; } kind = GREATER; break; case '.': in.get(); kind = DOT; break; case ',': in.get(); kind = COMMA; break; case ';': in.get(); kind = SEMI; break; default: { std::istream::pos_type pos = in.tellg(); // First, check to see if it's a reserved word, such as: and or not int result = parse_reserved_word(in); if (std::isalpha(c) && result == 1) break; // If not, rewind back to the beginning of the word to scan it // again. If the result was -1, it means no identifier was scanned // so we don't have to rewind. if (result == 0 || ! in.good()) { in.clear(); in.seekg(pos, std::ios::beg); if (in.fail()) throw_(parse_error, _("Failed to reset input stream")); } assert(in.good()); assert(! in.eof()); assert(static_cast<int>(in.tellg()) != -1); // When in relaxed parsing mode, we want to migrate commodity flags // so that any precision specified by the user updates the current // maximum displayed precision. parse_flags_t parse_flags; parse_flags.add_flags(PARSE_NO_ANNOT); if (pflags.has_flags(PARSE_NO_MIGRATE)) parse_flags.add_flags(PARSE_NO_MIGRATE); if (pflags.has_flags(PARSE_NO_REDUCE)) parse_flags.add_flags(PARSE_NO_REDUCE); try { amount_t temp; if (! temp.parse(in, parse_flags.plus_flags(PARSE_SOFT_FAIL))) { in.clear(); in.seekg(pos, std::ios::beg); if (in.fail() || ! in.good()) throw_(parse_error, _("Failed to reset input stream")); c = in.peek(); if (c != -1) { if (! std::isalpha(c) && c != '_') expected('\0', c); parse_ident(in); } else { throw_(parse_error, _("Unexpected EOF")); } if (! value.is_string() || value.as_string().empty()) { kind = ERROR; symbol[0] = c; symbol[1] = '\0'; throw_(parse_error, _("Failed to parse identifier")); } } else { if (! in.good()) { in.clear(); in.seekg(0, std::ios::end); if (in.fail()) throw_(parse_error, _("Failed to reset input stream")); } kind = VALUE; value = temp; length = static_cast<std::size_t>(in.tellg() - pos); } } catch (const std::exception&) { kind = ERROR; length = static_cast<std::size_t>(in.tellg() - pos); throw; } break; } } } void expr_t::token_t::rewind(std::istream& in) { in.clear(); in.seekg(- int(length), std::ios::cur); if (in.fail()) throw_(parse_error, _("Failed to rewind input stream")); } void expr_t::token_t::unexpected(const char wanted) { kind_t prev_kind = kind; kind = ERROR; if (wanted == '\0') { switch (prev_kind) { case TOK_EOF: throw_(parse_error, _("Unexpected end of expression")); case IDENT: throw_(parse_error, _f("Unexpected symbol '%1%'") % value); case VALUE: throw_(parse_error, _f("Unexpected value '%1%'") % value); default: throw_(parse_error, _f("Unexpected expression token '%1%'") % symbol); } } else { switch (prev_kind) { case TOK_EOF: throw_(parse_error, _f("Unexpected end of expression (wanted '%1%')") % wanted); case IDENT: throw_(parse_error, _f("Unexpected symbol '%1%' (wanted '%2%')") % value % wanted); case VALUE: throw_(parse_error, _f("Unexpected value '%1%' (wanted '%2%')") % value % wanted); default: throw_(parse_error, _f("Unexpected expression token '%1%' (wanted '%2%')") % symbol % wanted); } } } void expr_t::token_t::expected(const char wanted, const int c) { if (c == -1) { if (wanted == '\0') throw_(parse_error, _("Unexpected end")); else throw_(parse_error, _f("Missing '%1%'") % wanted); } else { char ch = c; if (wanted == '\0') throw_(parse_error, _f("Invalid char '%1%'") % ch); else throw_(parse_error, _f("Invalid char '%1%' (wanted '%2%')") % ch % wanted); } } void expr_t::token_t::expected(const kind_t wanted) { try { if (wanted == expr_t::token_t::ERROR || wanted == expr_t::token_t::UNKNOWN) throw_(parse_error, _f("Invalid token '%1%'") % *this); else throw_(parse_error, _f("Invalid token '%1%' (wanted '%2%')") % *this % wanted); } catch (...) { kind = ERROR; throw; } } std::ostream& operator<<(std::ostream& out, const expr_t::token_t::kind_t& kind) { switch (kind) { case expr_t::token_t::ERROR: out << "<error token>"; break; case expr_t::token_t::VALUE: out << "<value>"; break; case expr_t::token_t::IDENT: out << "<identifier>"; break; case expr_t::token_t::MASK: out << "<regex mask>"; break; case expr_t::token_t::LPAREN: out << "("; break; case expr_t::token_t::RPAREN: out << ")"; break; case expr_t::token_t::LBRACE: out << "{"; break; case expr_t::token_t::RBRACE: out << "}"; break; case expr_t::token_t::EQUAL: out << "=="; break; case expr_t::token_t::NEQUAL: out << "!="; break; case expr_t::token_t::LESS: out << "<"; break; case expr_t::token_t::LESSEQ: out << "<="; break; case expr_t::token_t::GREATER: out << ">"; break; case expr_t::token_t::GREATEREQ: out << ">="; break; case expr_t::token_t::ASSIGN: out << "="; break; case expr_t::token_t::MATCH: out << "=~"; break; case expr_t::token_t::NMATCH: out << "!~"; break; case expr_t::token_t::MINUS: out << "-"; break; case expr_t::token_t::PLUS: out << "+"; break; case expr_t::token_t::STAR: out << "*"; break; case expr_t::token_t::SLASH: out << "/"; break; case expr_t::token_t::ARROW: out << "->"; break; case expr_t::token_t::KW_DIV: out << "div"; break; case expr_t::token_t::EXCLAM: out << "!"; break; case expr_t::token_t::KW_AND: out << "and"; break; case expr_t::token_t::KW_OR: out << "or"; break; case expr_t::token_t::KW_MOD: out << "mod"; break; case expr_t::token_t::KW_IF: out << "if"; break; case expr_t::token_t::KW_ELSE: out << "else"; break; case expr_t::token_t::QUERY: out << "?"; break; case expr_t::token_t::COLON: out << ":"; break; case expr_t::token_t::DOT: out << "."; break; case expr_t::token_t::COMMA: out << ","; break; case expr_t::token_t::SEMI: out << ";"; break; case expr_t::token_t::TOK_EOF: out << "<end of input>"; break; case expr_t::token_t::UNKNOWN: out << "<unknown>"; break; } return out; } std::ostream& operator<<(std::ostream& out, const expr_t::token_t& token) { switch (token.kind) { case expr_t::token_t::VALUE: out << "<value '" << token.value << "'>"; break; case expr_t::token_t::IDENT: out << "<ident '" << token.value << "'>"; break; case expr_t::token_t::MASK: out << "<mask '" << token.value << "'>"; break; default: out << token.kind; break; } return out; } } // namespace ledger ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/token.h����������������������������������������������������������������������������0000664�0000000�0000000�00000010316�14411236400�0015035�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @defgroup expr Value expressions */ /** * @file token.h * @author John Wiegley * * @ingroup expr */ #ifndef INCLUDED_TOKEN_H #define INCLUDED_TOKEN_H #include "expr.h" namespace ledger { struct expr_t::token_t : public noncopyable { enum kind_t { ERROR, // an error occurred while tokenizing VALUE, // any kind of literal value IDENT, // [A-Za-z_]+ MASK, // /regexp/ LPAREN, // ( RPAREN, // ) LBRACE, // { RBRACE, // } EQUAL, // == NEQUAL, // != LESS, // < LESSEQ, // <= GREATER, // > GREATEREQ, // >= ASSIGN, // = MATCH, // =~ NMATCH, // !~ MINUS, // - PLUS, // + STAR, // * SLASH, // / ARROW, // -> KW_DIV, // div EXCLAM, // !, not KW_AND, // &, &&, and KW_OR, // |, ||, or KW_MOD, // % KW_IF, // if KW_ELSE, // else QUERY, // ? COLON, // : DOT, // . COMMA, // , SEMI, // ; TOK_EOF, UNKNOWN } kind; char symbol[6]; value_t value; std::size_t length; explicit token_t() : kind(UNKNOWN), length(0) { TRACE_CTOR(expr_t::token_t, ""); } ~token_t() throw() { TRACE_DTOR(expr_t::token_t); } token_t& operator=(const token_t& other) { if (&other == this) return *this; assert(false); // only one token object is used at a time return *this; } void clear() { kind = UNKNOWN; length = 0; value = NULL_VALUE; symbol[0] = '\0'; } int parse_reserved_word(std::istream& in); void parse_ident(std::istream& in); void next(std::istream& in, const parse_flags_t& flags); void rewind(std::istream& in); void unexpected(const char wanted = '\0'); void expected(const char wanted, const int c); void expected(const kind_t wanted); }; std::ostream& operator<<(std::ostream& out, const expr_t::token_t::kind_t& kind); std::ostream& operator<<(std::ostream& out, const expr_t::token_t& token); } // namespace ledger #endif // INCLUDED_TOKEN_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/unistring.h������������������������������������������������������������������������0000664�0000000�0000000�00000013570�14411236400�0015744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup utils */ /** * @file unistring.h * @author John Wiegley * * @ingroup utils */ #ifndef INCLUDED_UNISTRING_H #define INCLUDED_UNISTRING_H namespace ledger { int mk_wcwidth(boost::uint32_t ucs); /** * @class unistring * * @brief Abstract working with UTF-32 encoded Unicode strings * * The input to the string is a UTF8 encoded ledger::string, which can * then have its true length be taken, or characters extracted. */ class unistring { public: static const std::size_t npos = static_cast<std::size_t>(-1); std::vector<boost::uint32_t> utf32chars; unistring() { TRACE_CTOR(unistring, ""); } unistring(const std::string& input) { const char * p = input.c_str(); std::size_t len = input.length(); // This size should be at least as large as MAX_LINE in context.h assert(len < 4096); VERIFY(utf8::is_valid(p, p + len)); utf8::unchecked::utf8to32(p, p + len, std::back_inserter(utf32chars)); TRACE_CTOR(unistring, "std::string"); } ~unistring() { TRACE_DTOR(unistring); } std::size_t length() const { return utf32chars.size(); } std::size_t width() const { std::size_t width = 0; foreach (const boost::uint32_t& ch, utf32chars) { width += mk_wcwidth(ch); } return width; } std::string extract(const std::string::size_type begin = 0, const std::string::size_type len = 0) const { std::string utf8result; std::string::size_type this_len = length(); assert(begin <= this_len); assert(begin + len <= this_len); if (this_len) utf8::unchecked::utf32to8 (utf32chars.begin() + static_cast<std::string::difference_type>(begin), utf32chars.begin() + static_cast<std::string::difference_type>(begin) + static_cast<std::string::difference_type> (len ? (len > this_len ? this_len : len) : this_len), std::back_inserter(utf8result)); return utf8result; } std::string extract_by_width(std::string::size_type begin, std::size_t len) const { std::string utf8result; std::size_t this_width = width(); std::string::size_type this_len = length(); assert(begin <= this_width); if (begin + len > this_width) len = this_width - begin; std::size_t pos = 0; std::size_t begin_idx = 0, end_idx = 0; std::size_t head = 0, tail = 0; for (std::size_t idx = 0; idx < this_len; ++idx) { std::size_t w = mk_wcwidth(utf32chars[idx]); if (pos < begin) { if (pos + w >= begin) { head = std::min(pos + w, begin + len) - begin; begin_idx = idx + 1; } } else if (pos < begin + len) { if (pos + w > begin + len) { tail = begin + len - pos; end_idx = idx; } if (pos + w == begin + len) { tail = 0; end_idx = idx + 1; } } pos += w; } utf8result += std::string(head, '.'); if (begin_idx < end_idx) utf8::unchecked::utf32to8 (utf32chars.begin() + static_cast<std::string::difference_type>(begin_idx), utf32chars.begin() + static_cast<std::string::difference_type>(end_idx), std::back_inserter(utf8result)); utf8result += std::string(tail, '.'); return utf8result; } std::size_t find(const boost::uint32_t __s, std::size_t __pos = 0) const { std::size_t idx = 0; foreach (const boost::uint32_t& ch, utf32chars) { if (idx >= __pos && ch == __s) return idx; idx++; } return npos; } boost::uint32_t& operator[](const std::size_t index) { return utf32chars[index]; } const boost::uint32_t& operator[](const std::size_t index) const { return utf32chars[index]; } }; inline void justify(std::ostream& out, const std::string& str, int width, bool right = false, bool redden = false) { if (! right) { if (redden) out << "\033[31m"; out << str; if (redden) out << "\033[0m"; } unistring temp(str); int spacing = width - int(temp.width()); while (spacing-- > 0) out << ' '; if (right) { if (redden) out << "\033[31m"; out << str; if (redden) out << "\033[0m"; } } } // namespace ledger #endif // INCLUDED_UNISTRING_H ����������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/utils.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000054334�14411236400�0015223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "times.h" /********************************************************************** * * Assertions */ #if !NO_ASSERTS namespace ledger { DECLARE_EXCEPTION(assertion_failed, std::logic_error); void debug_assert(const string& reason, const string& func, const string& file, std::size_t line) { std::ostringstream buf; buf << "Assertion failed in " << file_context(file, line) << func << ": " << reason; throw assertion_failed(buf.str()); } } // namespace ledger #endif /********************************************************************** * * Verification (basically, very slow asserts) */ #if VERIFY_ON namespace ledger { bool verify_enabled = false; typedef std::pair<std::string, std::size_t> allocation_pair; typedef std::map<void *, allocation_pair> memory_map; typedef std::multimap<void *, allocation_pair> objects_map; typedef std::pair<std::size_t, std::size_t> count_size_pair; typedef std::map<std::string, count_size_pair> object_count_map; namespace { bool memory_tracing_active = false; memory_map * live_memory = NULL; memory_map * freed_memory = NULL; object_count_map * live_memory_count = NULL; object_count_map * total_memory_count = NULL; objects_map * live_objects = NULL; object_count_map * live_object_count = NULL; object_count_map * total_object_count = NULL; object_count_map * total_ctor_count = NULL; } void initialize_memory_tracing() { memory_tracing_active = false; live_memory = new memory_map; freed_memory = new memory_map; live_memory_count = new object_count_map; total_memory_count = new object_count_map; live_objects = new objects_map; live_object_count = new object_count_map; total_object_count = new object_count_map; total_ctor_count = new object_count_map; memory_tracing_active = true; } void shutdown_memory_tracing() { memory_tracing_active = false; if (live_objects) { IF_DEBUG("memory.counts") report_memory(std::cerr, true); else IF_DEBUG("memory.counts.live") report_memory(std::cerr); else if (live_objects->size() > 0) report_memory(std::cerr); } checked_delete(live_memory); live_memory = NULL; checked_delete(freed_memory); freed_memory = NULL; checked_delete(live_memory_count); live_memory_count = NULL; checked_delete(total_memory_count); total_memory_count = NULL; checked_delete(live_objects); live_objects = NULL; checked_delete(live_object_count); live_object_count = NULL; checked_delete(total_object_count); total_object_count = NULL; checked_delete(total_ctor_count); total_ctor_count = NULL; } inline void add_to_count_map(object_count_map& the_map, const char * name, std::size_t size) { object_count_map::iterator k = the_map.find(name); if (k != the_map.end()) { (*k).second.first++; (*k).second.second += size; } else { std::pair<object_count_map::iterator, bool> result = the_map.insert(object_count_map::value_type(name, count_size_pair(1, size))); VERIFY(result.second); } } std::size_t current_memory_size() { std::size_t memory_size = 0; foreach (const object_count_map::value_type& pair, *live_memory_count) memory_size += pair.second.second; return memory_size; } //#if !defined(__has_feature) || !__has_feature(address_sanitizer) static void trace_new_func(void * ptr, const char * which, std::size_t size) { if (! live_memory || ! memory_tracing_active) return; memory_tracing_active = false; memory_map::iterator i = freed_memory->find(ptr); if (i != freed_memory->end()) freed_memory->erase(i); live_memory->insert (memory_map::value_type(ptr, allocation_pair(which, size))); add_to_count_map(*live_memory_count, which, size); add_to_count_map(*total_memory_count, which, size); add_to_count_map(*total_memory_count, "__ALL__", size); memory_tracing_active = true; } static void trace_delete_func(void * ptr, const char * which) { if (! live_memory || ! memory_tracing_active) return; memory_tracing_active = false; // Ignore deletions of memory not tracked, since it's possible that // a user (like boost) allocated a block of memory before memory // tracking began, and then deleted it before memory tracking ended. // If it really is a double-delete, the malloc library on OS/X will // notify me. memory_map::iterator i = live_memory->find(ptr); if (i == live_memory->end()) { i = freed_memory->find(ptr); if (i != freed_memory->end()) VERIFY("Freeing a block of memory twice" == NULL); #if 0 // There can be memory allocated by Boost or the standard library, which // was allocated before memory tracing got turned on, that the system // might free for some coincidental reason. As such, we can't rely on // this check being valid. I've seen cases where processes ran to // completion with it on, and then others where valid processes failed. else VERIFY(! "Freeing an unknown block of memory"); #endif memory_tracing_active = true; return; } std::size_t size = (*i).second.second; VERIFY((*i).second.first == which); live_memory->erase(i); freed_memory->insert (memory_map::value_type(ptr, allocation_pair(which, size))); object_count_map::iterator j = live_memory_count->find(which); VERIFY(j != live_memory_count->end()); (*j).second.second -= size; if (--(*j).second.first == 0) live_memory_count->erase(j); memory_tracing_active = true; } //#endif // !defined(__has_feature) || !__has_feature(address_sanitizer) } // namespace ledger //#if !defined(__has_feature) || !__has_feature(address_sanitizer) #ifdef _GLIBCXX_THROW void * operator new(std::size_t size) _GLIBCXX_THROW(std::bad_alloc) { #else void * operator new(std::size_t size) throw (std::bad_alloc) { #endif void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new", size); return ptr; } void * operator new(std::size_t size, const std::nothrow_t&) throw() { void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new", size); return ptr; } #ifdef _GLIBCXX_THROW void * operator new[](std::size_t size) _GLIBCXX_THROW(std::bad_alloc) { #else void * operator new[](std::size_t size) throw (std::bad_alloc) { #endif void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new[]", size); return ptr; } void * operator new[](std::size_t size, const std::nothrow_t&) throw() { void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new[]", size); return ptr; } void operator delete(void * ptr) throw() { if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_delete_func(ptr, "new"); std::free(ptr); } void operator delete(void * ptr, const std::nothrow_t&) throw() { if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_delete_func(ptr, "new"); std::free(ptr); } void operator delete[](void * ptr) throw() { if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_delete_func(ptr, "new[]"); std::free(ptr); } void operator delete[](void * ptr, const std::nothrow_t&) throw() { if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_delete_func(ptr, "new[]"); std::free(ptr); } //#endif // !defined(__has_feature) || !__has_feature(address_sanitizer) namespace ledger { namespace { void stream_commified_number(std::ostream& out, std::size_t num) { std::ostringstream buf; std::ostringstream obuf; buf << num; string number(buf.str()); int integer_digits = 0; // Count the number of integer digits for (const char * p = number.c_str(); *p; p++) { if (*p == '.') break; else if (*p != '-') integer_digits++; } for (const char * p = number.c_str(); *p; p++) { if (*p == '.') { obuf << *p; assert(integer_digits <= 3); } else if (*p == '-') { obuf << *p; } else { obuf << *p; if (integer_digits > 3 && --integer_digits % 3 == 0) obuf << ','; } } out << obuf.str(); } void stream_memory_size(std::ostream& out, std::size_t size) { std::ostringstream obuf; if (size > 10 * 1024 * 1024) obuf << "\033[1m"; if (size > 100 * 1024 * 1024) obuf << "\033[31m"; obuf << std::setw(7); if (size < 1024) obuf << size << 'b'; else if (size < (1024 * 1024)) obuf << int(double(size) / 1024.0) << 'K'; else if (size < (1024 * 1024 * 1024)) obuf << int(double(size) / (1024.0 * 1024.0)) << 'M'; else obuf << int(double(size) / (1024.0 * 1024.0 * 1024.0)) << 'G'; if (size > 10 * 1024 * 1024) obuf << "\033[0m"; out << obuf.str(); } void report_count_map(std::ostream& out, object_count_map& the_map) { foreach (object_count_map::value_type& pair, the_map) { out << " " << std::right << std::setw(18); stream_commified_number(out, pair.second.first); out << " " << std::right << std::setw(7); stream_memory_size(out, pair.second.second); out << " " << std::left << pair.first << std::endl; } } } std::size_t current_objects_size() { std::size_t objects_size = 0; foreach (const object_count_map::value_type& pair, *live_object_count) objects_size += pair.second.second; return objects_size; } void trace_ctor_func(void * ptr, const char * cls_name, const char * args, std::size_t cls_size) { if (! live_objects || ! memory_tracing_active) return; memory_tracing_active = false; static char name[1024]; std::strcpy(name, cls_name); std::strcat(name, "("); std::strcat(name, args); std::strcat(name, ")"); DEBUG("memory.debug", "TRACE_CTOR " << ptr << " " << name); live_objects->insert (objects_map::value_type(ptr, allocation_pair(cls_name, cls_size))); add_to_count_map(*live_object_count, cls_name, cls_size); add_to_count_map(*total_object_count, cls_name, cls_size); add_to_count_map(*total_object_count, "__ALL__", cls_size); add_to_count_map(*total_ctor_count, name, cls_size); memory_tracing_active = true; } void trace_dtor_func(void * ptr, const char * cls_name, std::size_t cls_size) { if (! live_objects || ! memory_tracing_active) return; memory_tracing_active = false; DEBUG("memory.debug", "TRACE_DTOR " << ptr << " " << cls_name); objects_map::iterator i = live_objects->find(ptr); if (i == live_objects->end()) { warning_(_f("Attempting to delete %1% a non-living %2%") % ptr % cls_name); memory_tracing_active = true; return; } std::size_t ptr_count = live_objects->count(ptr); for (std::size_t x = 0; x < ptr_count; x++, i++) { if ((*i).second.first == cls_name) { live_objects->erase(i); break; } } object_count_map::iterator k = live_object_count->find(cls_name); if (k == live_object_count->end()) { warning_(_f("Failed to find %1% in live object counts") % cls_name); memory_tracing_active = true; return; } (*k).second.second -= cls_size; if (--(*k).second.first == 0) live_object_count->erase(k); memory_tracing_active = true; } void report_memory(std::ostream& out, bool report_all) { if (! live_memory) return; if (live_memory_count->size() > 0) { out << "NOTE: There may be memory held by Boost " << "and libstdc++ after ledger::shutdown()" << std::endl; out << "Live memory count:" << std::endl; report_count_map(out, *live_memory_count); } if (live_memory->size() > 0) { out << "Live memory:" << std::endl; foreach (const memory_map::value_type& pair, *live_memory) { out << " " << std::right << std::setw(18) << pair.first << " " << std::right << std::setw(7); stream_memory_size(out, pair.second.second); out << " " << std::left << pair.second.first << std::endl; } } if (report_all && total_memory_count->size() > 0) { out << "Total memory counts:" << std::endl; report_count_map(out, *total_memory_count); } if (live_object_count->size() > 0) { out << "Live object count:" << std::endl; report_count_map(out, *live_object_count); } if (live_objects->size() > 0) { out << "Live objects:" << std::endl; foreach (const objects_map::value_type& pair, *live_objects) { out << " " << std::right << std::setw(18) << pair.first << " " << std::right << std::setw(7); stream_memory_size(out, pair.second.second); out << " " << std::left << pair.second.first << std::endl; } } if (report_all) { if (total_object_count->size() > 0) { out << "Total object counts:" << std::endl; report_count_map(out, *total_object_count); } if (total_ctor_count->size() > 0) { out << "Total constructor counts:" << std::endl; report_count_map(out, *total_ctor_count); } } } } // namespace ledger #endif // VERIFY_ON /********************************************************************** * * String wrapper */ namespace ledger { string empty_string(""); strings_list split_arguments(const char * line) { strings_list args; char buf[4096]; char * q = buf; char in_quoted_string = '\0'; for (const char * p = line; *p; p++) { if (! in_quoted_string && std::isspace(*p)) { if (q != buf) { *q = '\0'; args.push_back(buf); q = buf; } } else if (in_quoted_string != '\'' && *p == '\\') { p++; if (! *p) throw_(std::logic_error, _("Invalid use of backslash")); *q++ = *p; } else if (in_quoted_string != '"' && *p == '\'') { if (in_quoted_string == '\'') in_quoted_string = '\0'; else in_quoted_string = '\''; } else if (in_quoted_string != '\'' && *p == '"') { if (in_quoted_string == '"') in_quoted_string = '\0'; else in_quoted_string = '"'; } else { *q++ = *p; } } if (in_quoted_string) throw_(std::logic_error, _f("Unterminated string, expected '%1%'") % in_quoted_string); if (q != buf) { *q = '\0'; args.push_back(buf); } return args; } } // namespace ledger /********************************************************************** * * Logging */ #if LOGGING_ON namespace ledger { log_level_t _log_level = LOG_WARN; std::ostream * _log_stream = &std::cerr; std::ostringstream _log_buffer; #if TRACING_ON uint16_t _trace_level; #endif static bool logger_has_run = false; static ptime logger_start; void logger_func(log_level_t level) { if (! logger_has_run) { logger_has_run = true; logger_start = TRUE_CURRENT_TIME(); #if VERIFY_ON IF_VERIFY() *_log_stream << " TIME OBJSZ MEMSZ" << std::endl; #endif } *_log_stream << std::right << std::setw(5) << (TRUE_CURRENT_TIME() - logger_start).total_milliseconds() << "ms"; #if VERIFY_ON IF_VERIFY() { *_log_stream << std::right << std::setw(6) << std::setprecision(3); stream_memory_size(*_log_stream, current_objects_size()); *_log_stream << std::right << std::setw(6) << std::setprecision(3); stream_memory_size(*_log_stream, current_memory_size()); } #endif *_log_stream << " " << std::left << std::setw(7); switch (level) { case LOG_CRIT: *_log_stream << "[CRIT]"; break; case LOG_FATAL: *_log_stream << "[FATAL]"; break; case LOG_ASSERT: *_log_stream << "[ASSRT]"; break; case LOG_ERROR: *_log_stream << "[ERROR]"; break; case LOG_VERIFY: *_log_stream << "[VERFY]"; break; case LOG_WARN: *_log_stream << "[WARN]"; break; case LOG_INFO: *_log_stream << "[INFO]"; break; case LOG_EXCEPT: *_log_stream << "[EXCPT]"; break; case LOG_DEBUG: *_log_stream << "[DEBUG]"; break; case LOG_TRACE: *_log_stream << "[TRACE]"; break; case LOG_OFF: case LOG_ALL: assert(false); break; } *_log_stream << ' ' << _log_buffer.str() << std::endl; _log_buffer.clear(); _log_buffer.str(""); } } // namespace ledger #if DEBUG_ON namespace ledger { optional<std::string> _log_category; #if HAVE_BOOST_REGEX_UNICODE optional<boost::u32regex> _log_category_re; #else optional<boost::regex> _log_category_re; #endif static struct __maybe_enable_debugging { __maybe_enable_debugging() { if (const char * p = std::getenv("LEDGER_DEBUG")) { _log_level = LOG_DEBUG; _log_category = p; } } } __maybe_enable_debugging_obj; } // namespace ledger #endif // DEBUG_ON #endif // LOGGING_ON /********************************************************************** * * Timers (allows log xacts to specify cumulative time spent) */ #if LOGGING_ON && defined(TIMERS_ON) namespace ledger { struct timer_t { log_level_t level; ptime begin; time_duration spent; std::string description; bool active; timer_t(log_level_t _level, std::string _description) : level(_level), begin(TRUE_CURRENT_TIME()), spent(time_duration(0, 0, 0, 0)), description(_description), active(true) {} }; typedef std::map<std::string, timer_t> timer_map; static timer_map timers; void start_timer(const char * name, log_level_t lvl) { #if VERIFY_ON bool tracing_active = memory_tracing_active; memory_tracing_active = false; #endif timer_map::iterator i = timers.find(name); if (i == timers.end()) { timers.insert(timer_map::value_type(name, timer_t(lvl, _log_buffer.str()))); } else { assert((*i).second.description == _log_buffer.str()); (*i).second.begin = TRUE_CURRENT_TIME(); (*i).second.active = true; } _log_buffer.clear(); _log_buffer.str(""); #if VERIFY_ON memory_tracing_active = tracing_active; #endif } void stop_timer(const char * name) { #if VERIFY_ON bool tracing_active = memory_tracing_active; memory_tracing_active = false; #endif timer_map::iterator i = timers.find(name); assert(i != timers.end()); (*i).second.spent += TRUE_CURRENT_TIME() - (*i).second.begin; (*i).second.active = false; #if VERIFY_ON memory_tracing_active = tracing_active; #endif } void finish_timer(const char * name) { #if VERIFY_ON bool tracing_active = memory_tracing_active; memory_tracing_active = false; #endif timer_map::iterator i = timers.find(name); if (i == timers.end()) { #if VERIFY_ON memory_tracing_active = tracing_active; #endif return; } time_duration spent = (*i).second.spent; if ((*i).second.active) { spent = TRUE_CURRENT_TIME() - (*i).second.begin; (*i).second.active = false; } _log_buffer << (*i).second.description << ' '; bool need_paren = (*i).second.description[(*i).second.description.size() - 1] != ':'; if (need_paren) _log_buffer << '('; _log_buffer << spent.total_milliseconds() << "ms"; if (need_paren) _log_buffer << ')'; logger_func((*i).second.level); timers.erase(i); #if VERIFY_ON memory_tracing_active = tracing_active; #endif } } // namespace ledger #endif // LOGGING_ON && TIMERS_ON /********************************************************************** * * Signal handlers */ caught_signal_t caught_signal = NONE_CAUGHT; void sigint_handler(int) { caught_signal = INTERRUPTED; } void sigpipe_handler(int) { caught_signal = PIPE_CLOSED; } /********************************************************************** * * General utility functions */ namespace ledger { path expand_path(const path& pathname) { if (pathname.empty()) return pathname; std::string path_string = pathname.string(); const char * pfx = NULL; string::size_type pos = path_string.find_first_of('/'); if (path_string.length() == 1 || pos == 1) { pfx = std::getenv("HOME"); #ifdef HAVE_GETPWUID if (! pfx) { // Punt. We're trying to expand ~/, but HOME isn't set struct passwd * pw = getpwuid(getuid()); if (pw) pfx = pw->pw_dir; } #endif } #ifdef HAVE_GETPWNAM else { string user(path_string, 1, pos == string::npos ? string::npos : pos - 1); struct passwd * pw = getpwnam(user.c_str()); if (pw) pfx = pw->pw_dir; } #endif // if we failed to find an expansion, return the path unchanged. if (! pfx) return pathname; string result(pfx); if (pos == string::npos) return result; if (result.length() == 0 || result[result.length() - 1] != '/') result += '/'; result += path_string.substr(pos + 1); return result; } path resolve_path(const path& pathname) { path temp = pathname; if (temp.string()[0] == '~') temp = expand_path(temp); #if (BOOST_VERSION >= 106000) temp.lexically_normal(); #else temp.normalize(); #endif return temp; } } // namespace ledger ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/utils.h����������������������������������������������������������������������������0000664�0000000�0000000�00000041145�14411236400�0015061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @defgroup util General utilities */ /** * @file utils.h * @author John Wiegley * * @ingroup util * * @brief General utility facilities used by Ledger */ #ifndef INCLUDED_UTILS_H #define INCLUDED_UTILS_H #if (BOOST_VERSION >= 106600) #include <boost/uuid/detail/sha1.hpp> #else #include <boost/uuid/sha1.hpp> #endif /** * @name Default values */ /*@{*/ #define TIMERS_ON 1 #if DEBUG_MODE #define DEBUG_ON 1 #define VERIFY_ON 1 #define TRACING_ON 1 #elif NO_ASSERTS #define DEBUG_ON 0 #define VERIFY_ON 0 #define TRACING_ON 0 //#define NO_LOGGING 1 #else #define DEBUG_ON 0 #define VERIFY_ON 0 #define TRACING_ON 1 // use --trace X to enable #endif /*@}*/ /** * @name Forward declarations */ /*@{*/ namespace ledger { using namespace boost; typedef std::string string; typedef std::list<string> strings_list; typedef posix_time::ptime ptime; typedef ptime::time_duration_type time_duration; typedef gregorian::date date; typedef gregorian::date_duration date_duration; typedef posix_time::seconds seconds; typedef boost::filesystem::path path; typedef boost::filesystem::ifstream ifstream; typedef boost::filesystem::ofstream ofstream; typedef boost::filesystem::filesystem_error filesystem_error; } /*@}*/ /** * @name Assertions */ /*@{*/ #ifdef assert #undef assert #endif #if !NO_ASSERTS namespace ledger { void debug_assert(const string& reason, const string& func, const string& file, std::size_t line); } #define assert(x) \ ((x) ? ((void)0) : debug_assert(#x, BOOST_CURRENT_FUNCTION, \ __FILE__, __LINE__)) #else // !NO_ASSERTS #define assert(x) #endif // !NO_ASSERTS /*@}*/ /** * @name Verification (i.e., heavy asserts) */ /*@{*/ #if VERIFY_ON namespace ledger { extern bool verify_enabled; #define VERIFY(x) if (ledger::verify_enabled) { assert(x); } #define DO_VERIFY() ledger::verify_enabled void initialize_memory_tracing(); void shutdown_memory_tracing(); std::size_t current_memory_size(); std::size_t current_objects_size(); void trace_ctor_func(void * ptr, const char * cls_name, const char * args, std::size_t cls_size); void trace_dtor_func(void * ptr, const char * cls_name, std::size_t cls_size); #define TRACE_CTOR(cls, args) \ (DO_VERIFY() ? \ ledger::trace_ctor_func(this, #cls, args, sizeof(cls)) : ((void)0)) #define TRACE_DTOR(cls) \ (DO_VERIFY() ? \ ledger::trace_dtor_func(this, #cls, sizeof(cls)) : ((void)0)) void report_memory(std::ostream& out, bool report_all = false); } // namespace ledger #else // ! VERIFY_ON #define VERIFY(x) #define DO_VERIFY() false #define TRACE_CTOR(cls, args) #define TRACE_DTOR(cls) #endif // VERIFY_ON #define IF_VERIFY() if (DO_VERIFY()) /*@}*/ /** * @name String wrapper * * This string type is a wrapper around std::string that allows us to trace * constructor and destructor calls. It also makes ledger's use of strings a * unique type, that the Boost.Python code can use as the basis for * transparent Unicode conversions. */ /*@{*/ namespace ledger { extern string empty_string; strings_list split_arguments(const char * line); inline string to_string(long num) { std::ostringstream buf; buf << num; return buf.str(); } inline string to_string(std::size_t num) { std::ostringstream buf; buf << num; return buf.str(); } inline string lowered(const string& str) { string tmp(str); to_lower(tmp); return tmp; } inline string operator+(const char * left, const string& right) { return string(left) + right; } } // namespace ledger /*@}*/ /** * @name Tracing and logging */ /*@{*/ #if ! defined(NO_LOGGING) #define LOGGING_ON 1 #endif #if LOGGING_ON namespace ledger { enum log_level_t { LOG_OFF = 0, LOG_CRIT, LOG_FATAL, LOG_ASSERT, LOG_ERROR, LOG_VERIFY, LOG_WARN, LOG_INFO, LOG_EXCEPT, LOG_DEBUG, LOG_TRACE, LOG_ALL }; extern log_level_t _log_level; extern std::ostream * _log_stream; extern std::ostringstream _log_buffer; void logger_func(log_level_t level); #define LOGGER(cat) \ static const char * const _this_category = cat #if TRACING_ON extern uint16_t _trace_level; #define SHOW_TRACE(lvl) \ (ledger::_log_level >= ledger::LOG_TRACE && lvl <= ledger::_trace_level) #define TRACE(lvl, msg) \ (SHOW_TRACE(lvl) ? \ ((ledger::_log_buffer << msg), \ ledger::logger_func(ledger::LOG_TRACE)) : (void)0) #else // TRACING_ON #define SHOW_TRACE(lvl) false #define TRACE(lvl, msg) #endif // TRACING_ON #if DEBUG_ON extern optional<std::string> _log_category; #if HAVE_BOOST_REGEX_UNICODE extern optional<boost::u32regex> _log_category_re; #else extern optional<boost::regex> _log_category_re; #endif inline bool category_matches(const char * cat) { if (_log_category) { if (! _log_category_re) { _log_category_re = #if HAVE_BOOST_REGEX_UNICODE boost::make_u32regex(_log_category->c_str(), boost::regex::perl | boost::regex::icase); #else boost::regex(_log_category->c_str(), boost::regex::perl | boost::regex::icase); #endif } #if HAVE_BOOST_REGEX_UNICODE return boost::u32regex_search(cat, *_log_category_re); #else return boost::regex_search(cat, *_log_category_re); #endif } return false; } #define SHOW_DEBUG(cat) \ (ledger::_log_level >= ledger::LOG_DEBUG && ledger::category_matches(cat)) #define SHOW_DEBUG_() SHOW_DEBUG(_this_category) #define DEBUG(cat, msg) \ (SHOW_DEBUG(cat) ? \ ((ledger::_log_buffer << msg), \ ledger::logger_func(ledger::LOG_DEBUG)) : (void)0) #define DEBUG_(msg) DEBUG(_this_category, msg) #else // DEBUG_ON #define SHOW_DEBUG(cat) false #define SHOW_DEBUG_() false #define DEBUG(cat, msg) #define DEBUG_(msg) #endif // DEBUG_ON #define LOG_MACRO(level, msg) \ (ledger::_log_level >= level ? \ ((ledger::_log_buffer << msg), ledger::logger_func(level)) : (void)0) #define SHOW_INFO() (ledger::_log_level >= ledger::LOG_INFO) #define SHOW_WARN() (ledger::_log_level >= ledger::LOG_WARN) #define SHOW_ERROR() (ledger::_log_level >= ledger::LOG_ERROR) #define SHOW_FATAL() (ledger::_log_level >= ledger::LOG_FATAL) #define SHOW_CRITICAL() (ledger::_log_level >= ledger::LOG_CRIT) #define INFO(msg) LOG_MACRO(ledger::LOG_INFO, msg) #define WARN(msg) LOG_MACRO(ledger::LOG_WARN, msg) #define ERROR(msg) LOG_MACRO(ledger::LOG_ERROR, msg) #define FATAL(msg) LOG_MACRO(ledger::LOG_FATAL, msg) #define CRITICAL(msg) LOG_MACRO(ledger::LOG_CRIT, msg) #define EXCEPTION(msg) LOG_MACRO(ledger::LOG_EXCEPT, msg) } // namespace ledger #else // ! LOGGING_ON #define LOGGER(cat) #define SHOW_TRACE(lvl) false #define SHOW_DEBUG(cat) false #define SHOW_DEBUG_() false #define SHOW_INFO() false #define SHOW_WARN() false #define SHOW_ERROR() false #define SHOW_FATAL() false #define SHOW_CRITICAL() false #define TRACE(lvl, msg) #define DEBUG(cat, msg) #define DEBUG_(msg) #define INFO(msg) #define WARN(msg) #define ERROR(msg) #define FATAL(msg) #define CRITICAL(msg) #endif // LOGGING_ON #define IF_TRACE(lvl) if (SHOW_TRACE(lvl)) #define IF_DEBUG(cat) if (SHOW_DEBUG(cat)) #define IF_DEBUG_() if (SHOW_DEBUG_()) #define IF_INFO() if (SHOW_INFO()) #define IF_WARN() if (SHOW_WARN()) #define IF_ERROR() if (SHOW_ERROR()) #define IF_FATAL() if (SHOW_FATAL()) #define IF_CRITICAL() if (SHOW_CRITICAL()) /*@}*/ /** * @name Timers * This allows log xacts to specify cumulative time spent. */ /*@{*/ #if LOGGING_ON && TIMERS_ON namespace ledger { void start_timer(const char * name, log_level_t lvl); void stop_timer(const char * name); void finish_timer(const char * name); #if TRACING_ON #define TRACE_START(name, lvl, msg) \ (SHOW_TRACE(lvl) ? \ ((ledger::_log_buffer << msg), \ ledger::start_timer(#name, ledger::LOG_TRACE)) : ((void)0)) #define TRACE_STOP(name, lvl) \ (SHOW_TRACE(lvl) ? ledger::stop_timer(#name) : ((void)0)) #define TRACE_FINISH(name, lvl) \ (SHOW_TRACE(lvl) ? ledger::finish_timer(#name) : ((void)0)) #else #define TRACE_START(name, lvl, msg) #define TRACE_STOP(name, lvl) #define TRACE_FINISH(name, lvl) #endif #if DEBUG_ON #define DEBUG_START(name, cat, msg) \ (SHOW_DEBUG(cat) ? \ ((ledger::_log_buffer << msg), \ ledger::start_timer(#name, ledger::LOG_DEBUG)) : ((void)0)) #define DEBUG_START_(name, msg) \ DEBUG_START_(name, _this_category, msg) #define DEBUG_STOP(name, cat) \ (SHOW_DEBUG(cat) ? ledger::stop_timer(#name) : ((void)0)) #define DEBUG_STOP_(name) \ DEBUG_STOP_(name, _this_category) #define DEBUG_FINISH(name, cat) \ (SHOW_DEBUG(cat) ? ledger::finish_timer(#name) : ((void)0)) #define DEBUG_FINISH_(name) \ DEBUG_FINISH_(name, _this_category) #else #define DEBUG_START(name, cat, msg) #define DEBUG_START_(name, msg) #define DEBUG_STOP(name) #define DEBUG_FINISH(name) #endif #define INFO_START(name, msg) \ (SHOW_INFO() ? \ ((ledger::_log_buffer << msg), \ ledger::start_timer(#name, ledger::LOG_INFO)) : ((void)0)) #define INFO_STOP(name) \ (SHOW_INFO() ? stop_timer(#name) : ((void)0)) #define INFO_FINISH(name) \ (SHOW_INFO() ? finish_timer(#name) : ((void)0)) } // namespace ledger #else // ! (LOGGING_ON && TIMERS_ON) #define TRACE_START(lvl, msg, name) #define TRACE_STOP(name, lvl) #define TRACE_FINISH(name, lvl) #define DEBUG_START(name, msg) #define DEBUG_START_(name, cat, msg) #define DEBUG_STOP(name) #define DEBUG_FINISH(name) #define INFO_START(name, msg) #define INFO_STOP(name) #define INFO_FINISH(name) #endif // TIMERS_ON /*@}*/ /* * These files define the other internal facilities. */ #include "error.h" enum caught_signal_t { NONE_CAUGHT, INTERRUPTED, PIPE_CLOSED }; extern caught_signal_t caught_signal; void sigint_handler(int sig); void sigpipe_handler(int sig); inline void check_for_signal() { switch (caught_signal) { case NONE_CAUGHT: break; case INTERRUPTED: throw std::runtime_error(_("Interrupted by user (use Control-D to quit)")); case PIPE_CLOSED: throw std::runtime_error(_("Pipe terminated")); } } /** * @name General utility functions */ /*@{*/ #define foreach BOOST_FOREACH using std::unique_ptr; namespace ledger { template <typename T, typename U> inline T& downcast(U& object) { return *polymorphic_downcast<T *>(&object); } path resolve_path(const path& pathname); inline const string& either_or(const string& first, const string& second) { return first.empty() ? second : first; } inline char * skip_ws(char * ptr) { while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') ptr++; return ptr; } inline char * trim_ws(char * ptr) { std::size_t len = std::strlen(ptr); int i = int(len) - 1; while (i >= 0 && (ptr[i] == ' ' || ptr[i] == '\t' || ptr[i] == '\n')) ptr[i--] = '\0'; return skip_ws(ptr); } inline char * next_element(char * buf, bool variable = false) { for (char * p = buf; *p; p++) { if (! (*p == ' ' || *p == '\t')) continue; if (! variable) { *p = '\0'; return skip_ws(p + 1); } else if (*p == '\t') { *p = '\0'; return skip_ws(p + 1); } else if (*(p + 1) == ' ') { *p = '\0'; return skip_ws(p + 2); } } return NULL; } inline int peek_next_nonws(std::istream& in) { int c = in.peek(); while (in.good() && ! in.eof() && std::isspace(c)) { in.get(); c = in.peek(); } return c; } #define READ_INTO(str, targ, size, var, cond) { \ char * _p = targ; \ var = str.peek(); \ while (str.good() && ! str.eof() && var != '\n' && \ (cond) && _p - targ < size) { \ var = str.get(); \ if (str.eof()) \ break; \ if (var == '\\') { \ var = str.get(); \ if (in.eof()) \ break; \ switch (var) { \ case 'b': var = '\b'; break; \ case 'f': var = '\f'; break; \ case 'n': var = '\n'; break; \ case 'r': var = '\r'; break; \ case 't': var = '\t'; break; \ case 'v': var = '\v'; break; \ default: break; \ } \ } \ *_p++ = var; \ var = str.peek(); \ } \ *_p = '\0'; \ } #define READ_INTO_(str, targ, size, var, idx, cond) { \ char * _p = targ; \ var = str.peek(); \ while (str.good() && ! str.eof() && var != '\n' && \ (cond) && _p - targ < size) { \ var = str.get(); \ if (str.eof()) \ break; \ idx++; \ if (var == '\\') { \ var = str.get(); \ if (in.eof()) \ break; \ switch (var) { \ case 'b': var = '\b'; break; \ case 'f': var = '\f'; break; \ case 'n': var = '\n'; break; \ case 'r': var = '\r'; break; \ case 't': var = '\t'; break; \ case 'v': var = '\v'; break; \ default: break; \ } \ idx++; \ } \ *_p++ = var; \ var = str.peek(); \ } \ *_p = '\0'; \ } inline string to_hex(unsigned int * message_digest, const int len = 1) { std::ostringstream buf; for(int i = 0; i < 5 ; i++) { buf.width(8); buf.fill('0'); buf << std::hex << message_digest[i]; if (i + 1 >= len) break; // only output the first LEN dwords } return buf.str(); } inline string sha1sum(const string& str) { boost::uuids::detail::sha1 sha; sha.process_bytes(str.c_str(), str.length()); unsigned int message_digest[5]; sha.get_digest(message_digest); return to_hex(message_digest, 5); } extern const string version; } // namespace ledger /*@}*/ #endif // INCLUDED_UTILS_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/value.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000135013�14411236400�0015171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "value.h" #include "commodity.h" #include "annotate.h" #include "pool.h" #include "unistring.h" // for justify() #include "op.h" namespace ledger { intrusive_ptr<value_t::storage_t> value_t::true_value; intrusive_ptr<value_t::storage_t> value_t::false_value; value_t::storage_t& value_t::storage_t::operator=(const value_t::storage_t& rhs) { type = rhs.type; switch (type) { case BALANCE: data = new balance_t(*boost::get<balance_t *>(rhs.data)); break; case SEQUENCE: data = new sequence_t(*boost::get<sequence_t *>(rhs.data)); break; default: data = rhs.data; break; } return *this; } void value_t::initialize() { true_value = new storage_t; true_value->type = BOOLEAN; true_value->data = true; false_value = new storage_t; false_value->type = BOOLEAN; false_value->data = false; } void value_t::shutdown() { true_value = intrusive_ptr<storage_t>(); false_value = intrusive_ptr<storage_t>(); } value_t::operator bool() const { switch (type()) { case VOID: return false; case BOOLEAN: return as_boolean(); case DATETIME: return is_valid(as_datetime()); case DATE: return is_valid(as_date()); case INTEGER: return as_long(); case AMOUNT: return as_amount(); case BALANCE: return as_balance(); case STRING: return ! as_string().empty(); case MASK: { std::ostringstream out; out << *this; throw_(value_error, _f("Cannot determine truth of %1% (did you mean 'account =~ %2%'?)") % label() % out.str()); } case SEQUENCE: if (! as_sequence().empty()) { foreach (const value_t& value, as_sequence()) { if (value) return true; } } return false; case SCOPE: return as_scope() != NULL; case ANY: return ! as_any().empty(); } add_error_context(_f("While taking boolean value of %1%:") % *this); throw_(value_error, _f("Cannot determine truth of %1%") % label()); return false; } void value_t::set_type(type_t new_type) { if (new_type == VOID) { storage.reset(); } else { if (! storage || storage->refc > 1) storage = new storage_t; else storage->destroy(); storage->type = new_type; } } bool value_t::to_boolean() const { if (is_boolean()) { return as_boolean(); } else { value_t temp(*this); temp.in_place_cast(BOOLEAN); return temp.as_boolean(); } } datetime_t value_t::to_datetime() const { if (is_datetime()) { return as_datetime(); } else { value_t temp(*this); temp.in_place_cast(DATETIME); return temp.as_datetime(); } } date_t value_t::to_date() const { if (is_date()) { return as_date(); } else { value_t temp(*this); temp.in_place_cast(DATE); return temp.as_date(); } } int value_t::to_int() const { if (is_long()) { return static_cast<int>(as_long()); } else { value_t temp(*this); temp.in_place_cast(INTEGER); return static_cast<int>(temp.as_long()); } } long value_t::to_long() const { if (is_long()) { return as_long(); } else { value_t temp(*this); temp.in_place_cast(INTEGER); return temp.as_long(); } } amount_t value_t::to_amount() const { if (is_amount()) { return as_amount(); } else { value_t temp(*this); temp.in_place_cast(AMOUNT); return temp.as_amount(); } } balance_t value_t::to_balance() const { if (is_balance()) { return as_balance(); } else { value_t temp(*this); temp.in_place_cast(BALANCE); return temp.as_balance(); } } string value_t::to_string() const { if (is_string()) { return as_string(); } else { value_t temp(*this); temp.in_place_cast(STRING); return temp.as_string(); } } mask_t value_t::to_mask() const { if (is_mask()) { return as_mask(); } else { value_t temp(*this); temp.in_place_cast(MASK); return temp.as_mask(); } } value_t::sequence_t value_t::to_sequence() const { if (is_sequence()) { return as_sequence(); } else { value_t temp(*this); temp.in_place_cast(SEQUENCE); return temp.as_sequence(); } } void value_t::in_place_simplify() { #if DEBUG_ON LOGGER("value.simplify"); #endif if (is_realzero()) { DEBUG_("Zeroing type " << static_cast<int>(type())); set_long(0L); return; } if (is_balance() && as_balance().single_amount()) { DEBUG_("Reducing balance to amount"); DEBUG_("as a balance it looks like: " << *this); in_place_cast(AMOUNT); DEBUG_("as an amount it looks like: " << *this); } #if REDUCE_TO_INTEGER // this is off by default if (is_amount() && ! as_amount().has_commodity() && as_amount().fits_in_long()) { DEBUG_("Reducing amount to integer"); in_place_cast(INTEGER); } #endif } value_t value_t::number() const { switch (type()) { case VOID: return 0L; case BOOLEAN: return as_boolean() ? 1L : 0L; case INTEGER: return as_long(); case AMOUNT: return as_amount().number(); case BALANCE: return as_balance().number(); case SEQUENCE: if (! as_sequence().empty()) { value_t temp; foreach (const value_t& value, as_sequence()) temp += value.number(); return temp; } break; default: break; } add_error_context(_f("While calling number() on %1%:") % *this); throw_(value_error, _f("Cannot determine numeric value of %1%") % label()); return false; } value_t& value_t::operator+=(const value_t& val) { if (is_string()) { if (val.is_string()) as_string_lval() += val.as_string(); else as_string_lval() += val.to_string(); return *this; } else if (is_sequence()) { if (val.is_sequence()) { if (size() == val.size()) { sequence_t::iterator i = begin(); sequence_t::const_iterator j = val.begin(); for (; i != end(); i++, j++) *i += *j; } else { add_error_context(_f("While adding %1% to %2%:") % val % *this); throw_(value_error, _("Cannot add sequences of different lengths")); } } else { as_sequence_lval().push_back(new value_t(val)); } return *this; } switch (type()) { case VOID: *this = value_t(val); return *this; case DATETIME: switch (val.type()) { case INTEGER: as_datetime_lval() += time_duration_t(0, 0, static_cast<time_duration_t::sec_type>(val.as_long())); return *this; case AMOUNT: as_datetime_lval() += time_duration_t(0, 0, static_cast<time_duration_t::sec_type> (val.as_amount().to_long())); return *this; default: break; } break; case DATE: switch (val.type()) { case INTEGER: as_date_lval() += gregorian::date_duration(val.as_long()); return *this; case AMOUNT: as_date_lval() += gregorian::date_duration(val.as_amount().to_long()); return *this; default: break; } break; case INTEGER: switch (val.type()) { case INTEGER: as_long_lval() += val.as_long(); return *this; case AMOUNT: if (val.as_amount().has_commodity()) { in_place_cast(BALANCE); return *this += val; } in_place_cast(AMOUNT); as_amount_lval() += val.as_amount(); return *this; case BALANCE: in_place_cast(BALANCE); as_balance_lval() += val.as_balance(); return *this; default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: if (as_amount().has_commodity()) { in_place_cast(BALANCE); return *this += val; } else { as_amount_lval() += val.as_long(); return *this; } case AMOUNT: if (as_amount().commodity() != val.as_amount().commodity()) { in_place_cast(BALANCE); return *this += val; } else { as_amount_lval() += val.as_amount(); return *this; } case BALANCE: in_place_cast(BALANCE); as_balance_lval() += val.as_balance(); return *this; default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: as_balance_lval() += val.to_amount(); return *this; case AMOUNT: as_balance_lval() += val.as_amount(); return *this; case BALANCE: as_balance_lval() += val.as_balance(); return *this; default: break; } break; default: break; } add_error_context(_f("While adding %1% to %2%:") % val % *this); throw_(value_error, _f("Cannot add %1% to %2%") % val.label() % label()); return *this; } value_t& value_t::operator-=(const value_t& val) { if (is_sequence()) { sequence_t& seq(as_sequence_lval()); if (val.is_sequence()) { if (size() == val.size()) { sequence_t::iterator i = begin(); sequence_t::const_iterator j = val.begin(); for (; i != end(); i++, j++) *i -= *j; } else { add_error_context(_f("While subtracting %1% from %2%:") % val % *this); throw_(value_error, _("Cannot subtract sequences of different lengths")); } } else { sequence_t::iterator i = std::find(seq.begin(), seq.end(), val); if (i != seq.end()) seq.erase(i); } return *this; } switch (type()) { case DATETIME: switch (val.type()) { case INTEGER: as_datetime_lval() -= time_duration_t(0, 0, static_cast<time_duration_t::sec_type>(val.as_long())); return *this; case AMOUNT: as_datetime_lval() -= time_duration_t(0, 0, static_cast<time_duration_t::sec_type> (val.as_amount().to_long())); return *this; default: break; } break; case DATE: switch (val.type()) { case INTEGER: as_date_lval() -= gregorian::date_duration(val.as_long()); return *this; case AMOUNT: as_date_lval() -= gregorian::date_duration(val.as_amount().to_long()); return *this; default: break; } break; case INTEGER: switch (val.type()) { case INTEGER: as_long_lval() -= val.as_long(); return *this; case AMOUNT: in_place_cast(AMOUNT); as_amount_lval() -= val.as_amount(); in_place_simplify(); return *this; case BALANCE: in_place_cast(BALANCE); as_balance_lval() -= val.as_balance(); in_place_simplify(); return *this; default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: if (as_amount().has_commodity()) { in_place_cast(BALANCE); *this -= val; in_place_simplify(); return *this; } else { as_amount_lval() -= val.as_long(); in_place_simplify(); return *this; } case AMOUNT: if (as_amount().commodity() != val.as_amount().commodity()) { in_place_cast(BALANCE); *this -= val; in_place_simplify(); return *this; } else { as_amount_lval() -= val.as_amount(); in_place_simplify(); return *this; } case BALANCE: in_place_cast(BALANCE); as_balance_lval() -= val.as_balance(); in_place_simplify(); return *this; default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: as_balance_lval() -= val.to_amount(); in_place_simplify(); return *this; case AMOUNT: as_balance_lval() -= val.as_amount(); in_place_simplify(); return *this; case BALANCE: as_balance_lval() -= val.as_balance(); in_place_simplify(); return *this; default: break; } break; default: break; } add_error_context(_f("While subtracting %1% from %2%:") % val % *this); throw_(value_error, _f("Cannot subtract %1% from %2%") % val.label() % label()); return *this; } value_t& value_t::operator*=(const value_t& val) { if (is_string()) { string temp; long count = val.to_long(); for (long i = 0; i < count; i++) temp += as_string(); set_string(temp); return *this; } else if (is_sequence()) { value_t temp; long count = val.to_long(); for (long i = 0; i < count; i++) temp += as_sequence(); return *this = temp; } switch (type()) { case INTEGER: switch (val.type()) { case INTEGER: as_long_lval() *= val.as_long(); return *this; case AMOUNT: set_amount(val.as_amount() * as_long()); return *this; default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: as_amount_lval() *= val.as_long(); return *this; case AMOUNT: as_amount_lval() *= val.as_amount(); return *this; case BALANCE: if (val.as_balance().single_amount()) { as_amount_lval() *= val.simplified().as_amount(); return *this; } break; default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: as_balance_lval() *= val.as_long(); return *this; case AMOUNT: if (as_balance().single_amount()) { in_place_simplify(); as_amount_lval() *= val.as_amount(); return *this; } else if (! val.as_amount().has_commodity()) { as_balance_lval() *= val.as_amount(); return *this; } break; default: break; } break; default: break; } add_error_context(_f("While multiplying %1% with %2%:") % val % *this); throw_(value_error, _f("Cannot multiply %1% with %2%") % label() % val.label()); return *this; } value_t& value_t::operator/=(const value_t& val) { switch (type()) { case INTEGER: switch (val.type()) { case INTEGER: as_long_lval() /= val.as_long(); return *this; case AMOUNT: set_amount(val.as_amount() / as_long()); return *this; default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: as_amount_lval() /= val.as_long(); return *this; case AMOUNT: as_amount_lval() /= val.as_amount(); return *this; case BALANCE: if (val.as_balance().single_amount()) { value_t simpler(val.simplified()); switch (simpler.type()) { case INTEGER: as_amount_lval() /= simpler.as_long(); break; case AMOUNT: as_amount_lval() /= simpler.as_amount(); break; default: assert(false); break; } return *this; } break; default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: as_balance_lval() /= val.as_long(); return *this; case AMOUNT: if (as_balance().single_amount()) { in_place_cast(AMOUNT); as_amount_lval() /= val.as_amount(); return *this; } else if (! val.as_amount().has_commodity()) { as_balance_lval() /= val.as_amount(); return *this; } break; default: break; } break; default: break; } add_error_context(_f("While dividing %1% by %2%:") % *this % val); throw_(value_error, _f("Cannot divide %1% by %2%") % label() % val.label()); return *this; } bool value_t::is_equal_to(const value_t& val) const { switch (type()) { case VOID: return val.type() == VOID; case BOOLEAN: if (val.is_boolean()) return as_boolean() == val.as_boolean(); break; case DATETIME: if (val.is_datetime()) return as_datetime() == val.as_datetime(); break; case DATE: if (val.is_date()) return as_date() == val.as_date(); break; case INTEGER: switch (val.type()) { case INTEGER: return as_long() == val.as_long(); case AMOUNT: return val.as_amount() == to_amount(); case BALANCE: return val.as_balance() == to_amount(); default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: return as_amount() == val.as_long(); case AMOUNT: return as_amount() == val.as_amount(); case BALANCE: return val.as_balance() == as_amount(); default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: return as_balance() == val.to_amount(); case AMOUNT: return as_balance() == val.as_amount(); case BALANCE: return as_balance() == val.as_balance(); default: break; } break; case STRING: if (val.is_string()) return as_string() == val.as_string(); break; case MASK: if (val.is_mask()) return as_mask() == val.as_mask(); break; case SEQUENCE: if (val.is_sequence()) return as_sequence() == val.as_sequence(); break; default: break; } add_error_context(_f("While comparing equality of %1% and %2%:") % *this % val); throw_(value_error, _f("Cannot compare %1% to %2%") % label() % val.label()); return *this; } bool value_t::is_less_than(const value_t& val) const { switch (type()) { case BOOLEAN: if (val.is_boolean()) { if (as_boolean()) { if (! val.as_boolean()) return false; else return false; } else if (! as_boolean()) { if (! val.as_boolean()) return false; else return true; } } break; case DATETIME: if (val.is_datetime()) return as_datetime() < val.as_datetime(); break; case DATE: if (val.is_date()) return as_date() < val.as_date(); break; case INTEGER: switch (val.type()) { case INTEGER: return as_long() < val.as_long(); case AMOUNT: return val.as_amount() > as_long(); case BALANCE: return val.to_amount() > as_long(); default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: return as_amount() < val.as_long(); case AMOUNT: if (as_amount().commodity() == val.as_amount().commodity() || ! as_amount().has_commodity() || ! val.as_amount().has_commodity()) return as_amount() < val.as_amount(); else return commodity_t::compare_by_commodity()(&as_amount(), &val.as_amount()) < 0; case BALANCE: return val.to_amount() > as_amount(); default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: case AMOUNT: { bool no_amounts = true; foreach (const balance_t::amounts_map::value_type& pair, as_balance().amounts) { if (pair.second >= val) return false; no_amounts = false; } return ! no_amounts; } case BALANCE: return val.to_amount() > to_amount(); default: break; } break; case STRING: if (val.is_string()) return as_string() < val.as_string(); break; case SEQUENCE: switch (val.type()) { case INTEGER: case AMOUNT: { bool no_amounts = true; foreach (const value_t& value, as_sequence()) { if (value >= val) return false; no_amounts = false; } return ! no_amounts; } case SEQUENCE: { sequence_t::const_iterator i = as_sequence().begin(); sequence_t::const_iterator j = val.as_sequence().begin(); for (; (i != as_sequence().end() && j != val.as_sequence().end()); i++, j++) { if (! ((*i) < (*j))) return false; } if (i == as_sequence().end()) return true; else return false; } default: break; } break; default: break; } add_error_context(_f("While comparing if %1% is less than %2%:") % *this % val); throw_(value_error, _f("Cannot compare %1% to %2%") % label() % val.label()); return *this; } bool value_t::is_greater_than(const value_t& val) const { switch (type()) { case BOOLEAN: if (val.is_boolean()) { if (as_boolean()) { if (! val.as_boolean()) return true; else return false; } else if (! as_boolean()) { if (! val.as_boolean()) return false; else return false; } } break; case DATETIME: if (val.is_datetime()) return as_datetime() > val.as_datetime(); break; case DATE: if (val.is_date()) return as_date() > val.as_date(); break; case INTEGER: switch (val.type()) { case INTEGER: return as_long() > val.as_long(); case AMOUNT: return val.as_amount() < as_long(); case BALANCE: return val.to_amount() < as_long(); default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: return as_amount() > val.as_long(); case AMOUNT: return as_amount() > val.as_amount(); case BALANCE: return val.to_amount() < as_amount(); default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: case AMOUNT: { bool no_amounts = true; foreach (const balance_t::amounts_map::value_type& pair, as_balance().amounts) { if (pair.second <= val) return false; no_amounts = false; } return ! no_amounts; } case BALANCE: return val.to_amount() < to_amount(); default: break; } break; case STRING: if (val.is_string()) return as_string() > val.as_string(); break; case SEQUENCE: switch (val.type()) { case INTEGER: case AMOUNT: { bool no_amounts = true; foreach (const value_t& value, as_sequence()) { if (value <= val) return false; no_amounts = false; } return ! no_amounts; } case SEQUENCE: { sequence_t::const_iterator i = as_sequence().begin(); sequence_t::const_iterator j = val.as_sequence().begin(); for (; (i != as_sequence().end() && j != val.as_sequence().end()); i++, j++) { if (! ((*i) > (*j))) return false; } if (i == as_sequence().end()) return false; else return true; } default: break; } break; default: break; } add_error_context(_f("While comparing if %1% is greater than %2%:") % *this % val); throw_(value_error, _f("Cannot compare %1% to %2%") % label() % val.label()); return *this; } void value_t::in_place_cast(type_t cast_type) { if (type() == cast_type) return; _dup(); if (cast_type == BOOLEAN) { set_boolean(bool(*this)); return; } else if (cast_type == SEQUENCE) { sequence_t temp; if (! is_null()) temp.push_back(new value_t(*this)); set_sequence(temp); return; } switch (type()) { case VOID: switch (cast_type) { case INTEGER: set_long(0L); return; case AMOUNT: set_amount(0L); return; case STRING: set_string(""); return; default: break; } break; case BOOLEAN: switch (cast_type) { case INTEGER: set_long(as_boolean() ? 1L : 0L); return; case AMOUNT: set_amount(as_boolean() ? 1L : 0L); return; case STRING: set_string(as_boolean() ? "true" : "false"); return; default: break; } break; case DATE: switch (cast_type) { case DATETIME: set_datetime(datetime_t(as_date(), time_duration(0, 0, 0, 0))); return; case STRING: set_string(format_date(as_date(), FMT_WRITTEN)); return; default: break; } break; case DATETIME: switch (cast_type) { case DATE: set_date(as_datetime().date()); return; case STRING: set_string(format_datetime(as_datetime(), FMT_WRITTEN)); return; default: break; } break; case INTEGER: switch (cast_type) { case AMOUNT: set_amount(as_long()); return; case BALANCE: set_balance(to_amount()); return; case STRING: set_string(lexical_cast<string>(as_long())); return; default: break; } break; case AMOUNT: { const amount_t& amt(as_amount()); switch (cast_type) { case INTEGER: if (amt.is_null()) set_long(0L); else set_long(as_amount().to_long()); return; case BALANCE: if (amt.is_null()) set_balance(balance_t()); else set_balance(as_amount()); return; case STRING: if (amt.is_null()) set_string(""); else set_string(as_amount().to_string()); return; default: break; } break; } case BALANCE: { const balance_t& bal(as_balance()); switch (cast_type) { case AMOUNT: { if (bal.amounts.size() == 1) { // Because we are changing the current balance value to an amount // value, and because set_amount takes a reference (and that memory is // about to be repurposed), we must pass in a copy. set_amount(amount_t((*bal.amounts.begin()).second)); return; } else if (bal.amounts.size() == 0) { set_amount(0L); return; } else { add_error_context(_f("While converting %1% to an amount:") % *this); throw_(value_error, _f("Cannot convert %1% with multiple commodities to %2%") % label() % label(cast_type)); } break; } case STRING: if (bal.is_empty()) set_string(""); else set_string(as_balance().to_string()); return; default: break; } break; } case STRING: switch (cast_type) { case INTEGER: { if (all(as_string(), is_any_of("-0123456789"))) { set_long(lexical_cast<long>(as_string())); return; } break; } case AMOUNT: set_amount(amount_t(as_string())); return; case DATE: set_date(parse_date(as_string())); return; case DATETIME: set_datetime(parse_datetime(as_string())); return; case MASK: set_mask(as_string()); return; default: break; } break; case MASK: switch (cast_type) { case STRING: set_string(as_mask().str()); return; default: break; } break; default: break; } add_error_context(_f("While converting %1%:") % *this); throw_(value_error, _f("Cannot convert %1% to %2%") % label() % label(cast_type)); } void value_t::in_place_negate() { switch (type()) { case BOOLEAN: set_boolean(! as_boolean()); return; case INTEGER: case DATETIME: set_long(- as_long()); return; case DATE: set_long(- as_long()); return; case AMOUNT: as_amount_lval().in_place_negate(); return; case BALANCE: as_balance_lval().in_place_negate(); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_negate(); return; default: break; } add_error_context(_f("While negating %1%:") % *this); throw_(value_error, _f("Cannot negate %1%") % label()); } void value_t::in_place_not() { switch (type()) { case BOOLEAN: set_boolean(! as_boolean()); return; case INTEGER: case DATETIME: set_boolean(! as_long()); return; case DATE: set_boolean(! as_long()); return; case AMOUNT: set_boolean(! as_amount()); return; case BALANCE: set_boolean(! as_balance()); return; case STRING: set_boolean(as_string().empty()); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_not(); return; default: break; } add_error_context(_f("While applying not to %1%:") % *this); throw_(value_error, _f("Cannot 'not' %1%") % label()); } bool value_t::is_realzero() const { switch (type()) { case BOOLEAN: return ! as_boolean(); case INTEGER: return as_long() == 0; case DATETIME: return ! is_valid(as_datetime()); case DATE: return ! is_valid(as_date()); case AMOUNT: return as_amount().is_realzero(); case BALANCE: return as_balance().is_realzero(); case STRING: return as_string().empty(); case SEQUENCE: return as_sequence().empty(); case SCOPE: return as_scope() == NULL; case ANY: return as_any().empty(); default: add_error_context(_f("While applying is_realzero to %1%:") % *this); throw_(value_error, _f("Cannot determine if %1% is really zero") % label()); } return false; } bool value_t::is_zero() const { switch (type()) { case BOOLEAN: return ! as_boolean(); case INTEGER: return as_long() == 0; case DATETIME: return ! is_valid(as_datetime()); case DATE: return ! is_valid(as_date()); case AMOUNT: return as_amount().is_zero(); case BALANCE: return as_balance().is_zero(); case STRING: return as_string().empty(); case SEQUENCE: return as_sequence().empty(); case SCOPE: return as_scope() == NULL; case ANY: return as_any().empty(); default: add_error_context(_f("While applying is_zero to %1%:") % *this); throw_(value_error, _f("Cannot determine if %1% is zero") % label()); } return false; } value_t value_t::value(const datetime_t& moment, const commodity_t * in_terms_of) const { switch (type()) { case INTEGER: return NULL_VALUE; case AMOUNT: if (optional<amount_t> val = as_amount().value(moment, in_terms_of)) return *val; return NULL_VALUE; case BALANCE: if (optional<balance_t> bal = as_balance().value(moment, in_terms_of)) return *bal; return NULL_VALUE; case SEQUENCE: { value_t temp; foreach (const value_t& value, as_sequence()) temp.push_back(value.value(moment, in_terms_of)); return temp; } default: break; } add_error_context(_f("While finding valuation of %1%:") % *this); throw_(value_error, _f("Cannot find the value of %1%") % label()); return NULL_VALUE; } value_t value_t::exchange_commodities(const std::string& commodities, const bool add_prices, const datetime_t& moment) { if (type() == SEQUENCE) { value_t temp; foreach (value_t& value, as_sequence_lval()) temp.push_back(value.exchange_commodities(commodities, add_prices, moment)); return temp; } // If we are repricing to just a single commodity, with no price // expression, skip the expensive logic below. if (commodities.find(',') == string::npos && commodities.find('=') == string::npos) return value(moment, commodity_pool_t::current_pool->find_or_create(commodities)); std::vector<commodity_t *> comms; std::vector<bool> force; typedef tokenizer<char_separator<char> > tokenizer; tokenizer tokens(commodities, char_separator<char>(",")); foreach (const string& name, tokens) { string::size_type name_len = name.length(); if (commodity_t * commodity = commodity_pool_t::current_pool ->parse_price_expression(name[name_len - 1] == '!' ? string(name, 0, name_len - 1) : name, add_prices, moment)) { DEBUG("commodity.exchange", "Pricing for commodity: " << commodity->symbol()); comms.push_back(&commodity->referent()); force.push_back(name[name_len - 1] == '!'); } } std::size_t index = 0; foreach (commodity_t * comm, comms) { switch (type()) { case AMOUNT: DEBUG("commodity.exchange", "We have an amount: " << as_amount_lval()); if (! force[index] && std::find(comms.begin(), comms.end(), &as_amount_lval().commodity().referent()) != comms.end()) break; DEBUG("commodity.exchange", "Referent doesn't match, pricing..."); if (optional<amount_t> val = as_amount_lval().value(moment, comm)) { DEBUG("commodity.exchange", "Re-priced amount is: " << *val); return *val; } DEBUG("commodity.exchange", "Was unable to find a price"); break; case BALANCE: { balance_t temp; bool repriced = false; DEBUG("commodity.exchange", "We have a balance: " << as_balance_lval()); foreach (const balance_t::amounts_map::value_type& pair, as_balance_lval().amounts) { DEBUG("commodity.exchange", "We have a balance amount of commodity: " << pair.first->symbol() << " == " << pair.second.commodity().symbol()); if (! force[index] && std::find(comms.begin(), comms.end(), &pair.first->referent()) != comms.end()) { temp += pair.second; } else { DEBUG("commodity.exchange", "Referent doesn't match, pricing..."); if (optional<amount_t> val = pair.second.value(moment, comm)) { DEBUG("commodity.exchange", "Re-priced member amount is: " << *val); temp += *val; repriced = true; } else { DEBUG("commodity.exchange", "Was unable to find price"); temp += pair.second; } } } if (repriced) { DEBUG("commodity.exchange", "Re-priced balance is: " << temp); return temp; } } default: break; } ++index; } return *this; } void value_t::in_place_reduce() { switch (type()) { case AMOUNT: as_amount_lval().in_place_reduce(); return; case BALANCE: as_balance_lval().in_place_reduce(); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_reduce(); return; default: return; } //throw_(value_error, _f("Cannot reduce %1%") % label()); } void value_t::in_place_unreduce() { switch (type()) { case AMOUNT: as_amount_lval().in_place_unreduce(); return; case BALANCE: as_balance_lval().in_place_unreduce(); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_unreduce(); return; default: return; } //throw_(value_error, _f("Cannot reduce %1%") % label()); } value_t value_t::abs() const { switch (type()) { case INTEGER: { long val = as_long(); if (val < 0) return - val; return val; } case AMOUNT: return as_amount().abs(); case BALANCE: return as_balance().abs(); default: break; } add_error_context(_f("While taking abs of %1%:") % *this); throw_(value_error, _f("Cannot abs %1%") % label()); return NULL_VALUE; } void value_t::in_place_round() { switch (type()) { case INTEGER: return; case AMOUNT: as_amount_lval().in_place_round(); return; case BALANCE: as_balance_lval().in_place_round(); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_round(); return; default: break; } add_error_context(_f("While rounding %1%:") % *this); throw_(value_error, _f("Cannot set rounding for %1%") % label()); } void value_t::in_place_roundto(int places) { DEBUG("amount.roundto", "=====> roundto places " << places); switch (type()) { case INTEGER: return; case AMOUNT: as_amount_lval().in_place_roundto(places); return; case BALANCE: as_balance_lval().in_place_roundto(places); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_roundto(places); return; default: break; } } void value_t::in_place_truncate() { switch (type()) { case INTEGER: return; case AMOUNT: as_amount_lval().in_place_truncate(); return; case BALANCE: as_balance_lval().in_place_truncate(); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_truncate(); return; default: break; } add_error_context(_f("While truncating %1%:") % *this); throw_(value_error, _f("Cannot truncate %1%") % label()); } void value_t::in_place_floor() { switch (type()) { case INTEGER: return; case AMOUNT: as_amount_lval().in_place_floor(); return; case BALANCE: as_balance_lval().in_place_floor(); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_floor(); return; default: break; } add_error_context(_f("While flooring %1%:") % *this); throw_(value_error, _f("Cannot floor %1%") % label()); } void value_t::in_place_ceiling() { switch (type()) { case INTEGER: return; case AMOUNT: as_amount_lval().in_place_ceiling(); return; case BALANCE: as_balance_lval().in_place_ceiling(); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_ceiling(); return; default: break; } add_error_context(_f("While ceiling %1%:") % *this); throw_(value_error, _f("Cannot ceiling %1%") % label()); } void value_t::in_place_unround() { switch (type()) { case INTEGER: return; case AMOUNT: as_amount_lval().in_place_unround(); return; case BALANCE: as_balance_lval().in_place_unround(); return; case SEQUENCE: foreach (value_t& value, as_sequence_lval()) value.in_place_unround(); return; default: break; } add_error_context(_f("While unrounding %1%:") % *this); throw_(value_error, _f("Cannot unround %1%") % label()); } void value_t::annotate(const annotation_t& details) { if (is_amount()) { as_amount_lval().annotate(details); } else { add_error_context(_f("While attempting to annotate %1%:") % *this); throw_(value_error, _f("Cannot annotate %1%") % label()); } } bool value_t::has_annotation() const { if (is_amount()) { return as_amount().has_annotation(); } else { add_error_context(_f("While checking if %1% has annotations:") % *this); throw_(value_error, _f("Cannot determine whether %1% is annotated") % label()); } return false; } annotation_t& value_t::annotation() { if (is_amount()) { return as_amount_lval().annotation(); } else { add_error_context(_f("While requesting the annotations of %1%:") % *this); throw_(value_error, _f("Cannot request annotation of %1%") % label()); return as_amount_lval().annotation(); // quiet g++ warning } } value_t value_t::strip_annotations(const keep_details_t& what_to_keep) const { if (what_to_keep.keep_all()) return *this; switch (type()) { case VOID: case BOOLEAN: case INTEGER: case DATETIME: case DATE: case STRING: case MASK: case SCOPE: case ANY: return *this; case SEQUENCE: { sequence_t temp; foreach (const value_t& value, as_sequence()) temp.push_back(new value_t(value.strip_annotations(what_to_keep))); return temp; } case AMOUNT: return as_amount().strip_annotations(what_to_keep); case BALANCE: return as_balance().strip_annotations(what_to_keep); } assert(false); return NULL_VALUE; } string value_t::label(optional<type_t> the_type) const { switch (the_type ? *the_type : type()) { case VOID: return _("an uninitialized value"); case BOOLEAN: return _("a boolean"); case DATETIME: return _("a date/time"); case DATE: return _("a date"); case INTEGER: return _("an integer"); case AMOUNT: return _("an amount"); case BALANCE: return _("a balance"); case STRING: return _("a string"); case MASK: return _("a regexp"); case SEQUENCE: return _("a sequence"); case SCOPE: return _("a scope"); case ANY: if (as_any().type() == typeid(expr_t::ptr_op_t)) return _("an expr"); else return _("an object"); } assert(false); return _("<invalid>"); } void value_t::print(std::ostream& _out, const int first_width, const int latter_width, const uint_least8_t flags) const { std::ostringstream out; if (first_width > 0 && (! is_amount() || as_amount().is_zero()) && ! is_balance() && ! is_string()) { out.width(first_width); if (flags & AMOUNT_PRINT_RIGHT_JUSTIFY) out << std::right; else out << std::left; } switch (type()) { case VOID: out << ""; break; case BOOLEAN: out << (as_boolean() ? "1" : "0"); break; case DATETIME: out << format_datetime(as_datetime(), FMT_WRITTEN); break; case DATE: out << format_date(as_date(), FMT_WRITTEN); break; case INTEGER: if (flags & AMOUNT_PRINT_COLORIZE && as_long() < 0) justify(out, to_string(), first_width, flags & AMOUNT_PRINT_RIGHT_JUSTIFY, true); else out << as_long(); break; case AMOUNT: { if (as_amount().is_zero()) { out << 0; } else { std::ostringstream buf; as_amount().print(buf, flags); justify(out, buf.str(), first_width, flags & AMOUNT_PRINT_RIGHT_JUSTIFY, flags & AMOUNT_PRINT_COLORIZE && as_amount().sign() < 0); } break; } case BALANCE: as_balance().print(out, first_width, latter_width, flags); break; case STRING: if (first_width > 0) justify(out, as_string(), first_width, flags & AMOUNT_PRINT_RIGHT_JUSTIFY); else out << as_string(); break; case MASK: out << '/' << as_mask() << '/'; break; case SEQUENCE: { out << '('; bool first = true; foreach (const value_t& value, as_sequence()) { if (first) first = false; else out << ", "; value.print(out, first_width, latter_width, flags); } out << ')'; break; } case SCOPE: out << "<#SCOPE>"; break; case ANY: if (as_any().type() == typeid(expr_t::ptr_op_t)) { out << "<#EXPR "; as_any<expr_t::ptr_op_t>()->print(out); out << ">"; } else { out << "<#OBJECT>"; } break; } _out << out.str(); } void value_t::dump(std::ostream& out, const bool relaxed) const { switch (type()) { case VOID: out << "null"; break; case BOOLEAN: if (as_boolean()) out << "true"; else out << "false"; break; case DATETIME: out << '[' << format_datetime(as_datetime(), FMT_WRITTEN) << ']'; break; case DATE: out << '[' << format_date(as_date(), FMT_WRITTEN) << ']'; break; case INTEGER: out << as_long(); break; case AMOUNT: if (! relaxed) out << '{'; out << as_amount(); if (! relaxed) out << '}'; break; case BALANCE: out << as_balance(); break; case STRING: out << '"'; foreach (const char& ch, as_string()) { switch (ch) { case '"': out << "\\\""; break; case '\\': out << "\\\\"; break; default: out << ch; break; } } out << '"'; break; case MASK: out << '/' << as_mask() << '/'; break; case SCOPE: out << as_scope(); break; case ANY: if (as_any().type() == typeid(expr_t::ptr_op_t)) as_any<expr_t::ptr_op_t>()->dump(out); else out << boost::unsafe_any_cast<const void *>(&as_any()); break; case SEQUENCE: { out << '('; bool first = true; foreach (const value_t& value, as_sequence()) { if (first) first = false; else out << ", "; value.dump(out, relaxed); } out << ')'; break; } } } bool value_t::valid() const { switch (type()) { case AMOUNT: return as_amount().valid(); case BALANCE: return as_balance().valid(); default: break; } return true; } bool sort_value_is_less_than(const std::list<sort_value_t>& left_values, const std::list<sort_value_t>& right_values) { std::list<sort_value_t>::const_iterator left_iter = left_values.begin(); std::list<sort_value_t>::const_iterator right_iter = right_values.begin(); while (left_iter != left_values.end() && right_iter != right_values.end()) { // Don't even try to sort balance values if (! (*left_iter).value.is_balance() && ! (*right_iter).value.is_balance()) { DEBUG("value.sort", " Comparing " << (*left_iter).value << " < " << (*right_iter).value); if ((*left_iter).value < (*right_iter).value) { DEBUG("value.sort", " is less"); return ! (*left_iter).inverted; } else if ((*left_iter).value > (*right_iter).value) { DEBUG("value.sort", " is greater"); return (*left_iter).inverted; } } left_iter++; right_iter++; } assert(left_iter == left_values.end()); assert(right_iter == right_values.end()); return false; } void put_value(property_tree::ptree& pt, const value_t& value) { switch (value.type()) { case value_t::VOID: pt.add("void", ""); break; case value_t::BOOLEAN: pt.add("bool", value.as_boolean() ? "true" : "false"); break; case value_t::INTEGER: pt.add("int", value.to_string()); break; case value_t::AMOUNT: put_amount(pt.add("amount", ""), value.as_amount()); break; case value_t::BALANCE: put_balance(pt.add("balance", ""), value.as_balance()); break; case value_t::DATETIME: put_datetime(pt.add("datetime", ""), value.as_datetime()); break; case value_t::DATE: put_date(pt.add("date", ""), value.as_date()); break; case value_t::STRING: pt.add("string", value.as_string()); break; case value_t::MASK: put_mask(pt.add("mask", ""), value.as_mask()); break; case value_t::SEQUENCE: { property_tree::ptree& st(pt.add("sequence", "")); foreach (const value_t& member, value.as_sequence()) put_value(st, member); break; } case value_t::SCOPE: case value_t::ANY: assert(false); break; } } } // namespace ledger ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/value.h����������������������������������������������������������������������������0000664�0000000�0000000�00000064763�14411236400�0015050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup math */ /** * @file value.h * @author John Wiegley * * @ingroup math * * @brief Abstract dynamic type representing various numeric types * * A value_t object can be one of many types, and changes its type * dynamically based on how it is used. For example, if you assign * the number 10 to a value object, it's internal type will be * INTEGER. */ #ifndef INCLUDED_VALUE_H #define INCLUDED_VALUE_H #include "balance.h" // includes amount.h #include "mask.h" namespace ledger { DECLARE_EXCEPTION(value_error, std::runtime_error); class scope_t; /** * @class value_t * * @brief Dynamic type representing various numeric types. * * The following type is a polymorphous value type used solely for * performance reasons. The alternative is to compute value * expressions (valexpr.cc) in terms of the largest data type, * balance_t. This was found to be prohibitively expensive, especially * when large logic chains were involved, since many temporary * allocations would occur for every operator. With value_t, and the * fact that logic chains only need boolean values to continue, no * memory allocations need to take place at all. */ class value_t : public ordered_field_operators<value_t, equality_comparable<value_t, balance_t, additive<value_t, balance_t, multiplicative<value_t, balance_t, ordered_field_operators<value_t, amount_t, ordered_field_operators<value_t, double, ordered_field_operators<value_t, unsigned long, ordered_field_operators<value_t, long> > > > > > > > { public: /** * The sequence_t member type abstracts the type used to represent a * resizable "array" of value_t objects. */ typedef ptr_deque<value_t> sequence_t; typedef sequence_t::iterator iterator; typedef sequence_t::const_iterator const_iterator; typedef sequence_t::difference_type difference_type; /** * type_t gives the type of the data contained or referenced by a * value_t object. Use the type() method to get a value of type * type_t. */ enum type_t { VOID, // a null value (i.e., uninitialized) BOOLEAN, // a boolean DATETIME, // a date and time (Boost posix_time) DATE, // a date (Boost gregorian::date) INTEGER, // a signed integer value AMOUNT, // a ledger::amount_t BALANCE, // a ledger::balance_t STRING, // a string object MASK, // a regular expression mask SEQUENCE, // a vector of value_t objects SCOPE, // a pointer to a scope ANY // a pointer to an arbitrary object }; class storage_t { friend class value_t; /** * The `data' member holds the actual bytes relating to whatever * has been stuffed into this storage object. There is a set of * asserts in value.cc to guarantee that the sizeof expression * used here is indeed at least as big as the largest object that * will ever be copied into `data'. * * The `type' member holds the value_t::type_t value representing * the type of the object stored. */ variant<bool, // BOOLEAN datetime_t, // DATETIME date_t, // DATE long, // INTEGER amount_t, // AMOUNT balance_t *, // BALANCE string, // STRING mask_t, // MASK sequence_t *, // SEQUENCE scope_t *, // SCOPE boost::any // ANY > data; type_t type; /** * `refc' holds the current reference count for each storage_t * object. */ mutable int refc; /** * Constructor. Since all storage object are assigned to after * construction, the only constructors allowed are explicit, and * copy (see below). The default starting type is VOID, which * should rarely ever be seen in practice, since the first thing * that value_t typically does is to assign a valid value. */ explicit storage_t() : type(VOID), refc(0) { TRACE_CTOR(value_t::storage_t, ""); } public: // so `checked_delete' can access it /** * Destructor. Must only be called when the reference count has * reached zero. The `destroy' method is used to do the actual * cleanup of the data, since it's quite possible for `destroy' to * be called while the object is still active -- to clear the * stored data for subsequent reuse of the storage_t object. */ ~storage_t() { TRACE_DTOR(value_t::storage_t); VERIFY(refc == 0); destroy(); } private: /** * Assignment and copy operators. These are called when making a * new copy of a storage object in order to modify the copy. */ explicit storage_t(const storage_t& rhs) : type(rhs.type), refc(0) { *this = rhs; TRACE_CTOR(value_t::storage_t, "copy"); } storage_t& operator=(const storage_t& rhs); /** * Reference counting methods. The intrusive_ptr_* methods are * used by boost::intrusive_ptr to manage the calls to acquire and * release. */ void acquire() const { DEBUG("value.storage.refcount", "Acquiring " << this << ", refc now " << refc + 1); VERIFY(refc >= 0); refc++; } void release() const { DEBUG("value.storage.refcount", "Releasing " << this << ", refc now " << refc - 1); VERIFY(refc > 0); if (--refc == 0) checked_delete(this); } friend inline void intrusive_ptr_add_ref(value_t::storage_t * storage_ptr) { storage_ptr->acquire(); } friend inline void intrusive_ptr_release(value_t::storage_t * storage_ptr) { storage_ptr->release(); } void destroy() { DEBUG("value.storage.refcount", "Destroying " << this); switch (type) { case VOID: return; case BALANCE: checked_delete(boost::get<balance_t *>(data)); break; case SEQUENCE: checked_delete(boost::get<sequence_t *>(data)); break; default: break; } data = false; type = VOID; } }; private: /** * The actual data for each value_t is kept in reference counted storage. * Data is modified using a copy-on-write policy. */ intrusive_ptr<storage_t> storage; /** * Make a private copy of the current value (if necessary) so it can * subsequently be modified. */ void _dup() { if (storage && storage->refc > 1) storage = new storage_t(*storage.get()); } /** * Because boolean "true" and "false" are so common, a pair of static * references are kept to prevent the creation of throwaway storage_t * objects just to represent these two common values. */ static intrusive_ptr<storage_t> true_value; static intrusive_ptr<storage_t> false_value; public: static void initialize(); static void shutdown(); public: /** * Constructors. value_t objects may be constructed from almost any * value type that they can contain, including variations on those * types (such as long, unsigned long, etc). The ordering of the * methods here reflects the ordering of the constants in type_t * above. * * One constructor of special note is that taking a string or * character pointer as an argument. Because value_t("$100") is * interpreted as a commoditized amount, the form value_t("$100", * true) is required to represent the literal string "$100", and not * the amount "one hundred dollars". */ value_t() { TRACE_CTOR(value_t, ""); } value_t(const bool val) { set_boolean(val); TRACE_CTOR(value_t, "const bool"); } value_t(const datetime_t& val) { set_datetime(val); TRACE_CTOR(value_t, "const datetime_t&"); } value_t(const date_t& val) { set_date(val); TRACE_CTOR(value_t, "const date_t&"); } value_t(const long val) { set_long(val); TRACE_CTOR(value_t, "const long"); } value_t(const unsigned long val) { set_amount(val); TRACE_CTOR(value_t, "const unsigned long"); } value_t(const double val) { set_amount(val); TRACE_CTOR(value_t, "const double"); } value_t(const amount_t& val) { set_amount(val); TRACE_CTOR(value_t, "const amount_t&"); } value_t(const balance_t& val) { set_balance(val); TRACE_CTOR(value_t, "const balance_t&"); } value_t(const mask_t& val) { set_mask(val); TRACE_CTOR(value_t, "const mask_t&"); } explicit value_t(const string& val, bool literal = false) { if (literal) set_string(val); else set_amount(amount_t(val)); TRACE_CTOR(value_t, "const string&, bool"); } explicit value_t(const char * val, bool literal = false) { if (literal) set_string(val); else set_amount(amount_t(val)); TRACE_CTOR(value_t, "const char *"); } value_t(const sequence_t& val) { set_sequence(val); TRACE_CTOR(value_t, "const sequence_t&"); } explicit value_t(scope_t * item) { set_scope(item); TRACE_CTOR(value_t, "scope_t *"); } #if 0 template <typename T> explicit value_t(T& item) { set_any(item); TRACE_CTOR(value_t, "T&"); } #endif /** * Destructor. This does not do anything, because the intrusive_ptr * that refers to our storage object will decrease its reference * count itself upon destruction. */ ~value_t() { TRACE_DTOR(value_t); } /** * Assignment and copy operators. Values are cheaply copied by * simply creating another reference to the other value's storage * object. A true copy is only ever made prior to modification. */ value_t(const value_t& val) { *this = val; TRACE_CTOR(value_t, "copy"); } value_t& operator=(const value_t& val) { if (! (this == &val || storage == val.storage)) storage = val.storage; return *this; } /** * Comparison operators. Values can be compared to other values */ bool is_equal_to(const value_t& val) const; bool is_less_than(const value_t& val) const; bool is_greater_than(const value_t& val) const; template <typename T> bool operator==(const T& amt) const { return is_equal_to(amt); } template <typename T> bool operator<(const T& amt) const { return is_less_than(amt); } template <typename T> bool operator>(const T& amt) const { return is_greater_than(amt); } /** * Binary arithmetic operators. * * add(amount_t, optional<amount_t>) allows for the possibility of * adding both an amount and its cost in a single operation. * Otherwise, there is no way to separately represent the "cost" * part of an amount addition statement. */ value_t& operator+=(const value_t& val); value_t& operator-=(const value_t& val); value_t& operator*=(const value_t& val); value_t& operator/=(const value_t& val); /** * Unary arithmetic operators. */ value_t negated() const { value_t temp = *this; temp.in_place_negate(); return temp; } void in_place_negate(); // exists for efficiency's sake void in_place_not(); // exists for efficiency's sake value_t operator-() const { return negated(); } value_t abs() const; value_t rounded() const { value_t temp(*this); temp.in_place_round(); return temp; } void in_place_round(); value_t roundto(int places) const { value_t temp(*this); temp.in_place_roundto(places); return temp; } void in_place_roundto(int places); value_t truncated() const { value_t temp(*this); temp.in_place_truncate(); return temp; } void in_place_truncate(); value_t floored() const { value_t temp(*this); temp.in_place_floor(); return temp; } void in_place_floor(); value_t ceilinged() const { value_t temp(*this); temp.in_place_ceiling(); return temp; } void in_place_ceiling(); value_t unrounded() const { value_t temp(*this); temp.in_place_unround(); return temp; } void in_place_unround(); value_t reduced() const { value_t temp(*this); temp.in_place_reduce(); return temp; } void in_place_reduce(); // exists for efficiency's sake value_t unreduced() const { value_t temp(*this); temp.in_place_unreduce(); return temp; } void in_place_unreduce(); // exists for efficiency's sake // Return the "market value" of a given value at a specific time. value_t value(const datetime_t& moment = datetime_t(), const commodity_t * in_terms_of = NULL) const; value_t exchange_commodities(const std::string& commodities, const bool add_prices = false, const datetime_t& moment = datetime_t()); /** * Truth tests. */ operator bool() const; bool is_nonzero() const { return ! is_zero(); } bool is_realzero() const; bool is_zero() const; bool is_null() const { if (! storage) { VERIFY(is_type(VOID)); return true; } else { VERIFY(! is_type(VOID)); return false; } } type_t type() const { return storage ? storage->type : VOID; } bool is_type(type_t _type) const { return type() == _type; } private: void set_type(type_t new_type); public: /** * Data manipulation methods. A value object may be truth tested for the * existence of every type it can contain: * * is_boolean() * is_long() * is_datetime() * is_date() * is_amount() * is_balance() * is_string() * is_mask() * is_sequence() * is_any() * * There are corresponding as_*() methods that represent a value as a * reference to its underlying type. For example, as_long() returns a * reference to a "const long". * * There are also as_*_lval() methods, which represent the underlying data * as a reference to a non-const type. The difference here is that an * _lval() call causes the underlying data to be fully copied before the * resulting reference is returned. * * Lastly, there are corresponding set_*(data) methods for directly * assigning data of a particular type, rather than using the regular * assignment operator (whose implementation simply calls the various set_ * methods). */ bool is_boolean() const { return is_type(BOOLEAN); } bool& as_boolean_lval() { VERIFY(is_boolean()); _dup(); return boost::get<bool>(storage->data); } const bool& as_boolean() const { VERIFY(is_boolean()); return boost::get<bool>(storage->data); } void set_boolean(const bool val) { set_type(BOOLEAN); storage = val ? true_value : false_value; } bool is_datetime() const { return is_type(DATETIME); } datetime_t& as_datetime_lval() { VERIFY(is_datetime()); _dup(); return boost::get<datetime_t>(storage->data); } const datetime_t& as_datetime() const { VERIFY(is_datetime()); return boost::get<datetime_t>(storage->data); } void set_datetime(const datetime_t& val) { set_type(DATETIME); storage->data = val; } bool is_date() const { return is_type(DATE); } date_t& as_date_lval() { VERIFY(is_date()); _dup(); return boost::get<date_t>(storage->data); } const date_t& as_date() const { VERIFY(is_date()); return boost::get<date_t>(storage->data); } void set_date(const date_t& val) { set_type(DATE); storage->data = val; } bool is_long() const { return is_type(INTEGER); } long& as_long_lval() { VERIFY(is_long()); _dup(); return boost::get<long>(storage->data); } const long& as_long() const { VERIFY(is_long()); return boost::get<long>(storage->data); } void set_long(const long val) { set_type(INTEGER); storage->data = val; } bool is_amount() const { return is_type(AMOUNT); } amount_t& as_amount_lval() { VERIFY(is_amount()); _dup(); return boost::get<amount_t>(storage->data); } const amount_t& as_amount() const { VERIFY(is_amount()); return boost::get<amount_t>(storage->data); } void set_amount(const amount_t& val) { VERIFY(val.valid()); set_type(AMOUNT); storage->data = val; } bool is_balance() const { return is_type(BALANCE); } balance_t& as_balance_lval() { VERIFY(is_balance()); _dup(); return *boost::get<balance_t *>(storage->data); } const balance_t& as_balance() const { VERIFY(is_balance()); return *boost::get<balance_t *>(storage->data); } void set_balance(const balance_t& val) { VERIFY(val.valid()); set_type(BALANCE); storage->data = new balance_t(val); } bool is_string() const { return is_type(STRING); } string& as_string_lval() { VERIFY(is_string()); _dup(); return boost::get<string>(storage->data); } const string& as_string() const { VERIFY(is_string()); return boost::get<string>(storage->data); } void set_string(const string& val = "") { set_type(STRING); storage->data = val; VERIFY(boost::get<string>(storage->data) == val); } void set_string(const char * val = "") { set_type(STRING); storage->data = string(val); VERIFY(boost::get<string>(storage->data) == val); } bool is_mask() const { return is_type(MASK); } mask_t& as_mask_lval() { VERIFY(is_mask()); _dup(); VERIFY(boost::get<mask_t>(storage->data).valid()); return boost::get<mask_t>(storage->data); } const mask_t& as_mask() const { VERIFY(is_mask()); VERIFY(boost::get<mask_t>(storage->data).valid()); return boost::get<mask_t>(storage->data); } void set_mask(const string& val) { set_type(MASK); storage->data = mask_t(val); } void set_mask(const mask_t& val) { set_type(MASK); storage->data = val; } bool is_sequence() const { return is_type(SEQUENCE); } sequence_t& as_sequence_lval() { VERIFY(is_sequence()); _dup(); return *boost::get<sequence_t *>(storage->data); } const sequence_t& as_sequence() const { VERIFY(is_sequence()); return *boost::get<sequence_t *>(storage->data); } void set_sequence(const sequence_t& val) { set_type(SEQUENCE); storage->data = new sequence_t(val); } /** * Dealing with scope pointers. */ bool is_scope() const { return is_type(SCOPE); } scope_t * as_scope() const { VERIFY(is_scope()); return boost::get<scope_t *>(storage->data); } void set_scope(scope_t * val) { set_type(SCOPE); storage->data = val; } /** * Dealing with any type at all is bit involved because we actually * deal with typed object. For example, if you call as_any it returns * a boost::any object, but if you use as_any<type_t>, then it returns * a type_t by value. */ bool is_any() const { return is_type(ANY); } template <typename T> bool is_any() const { return (is_type(ANY) && boost::get<boost::any>(storage->data).type() == typeid(T)); } boost::any& as_any_lval() { VERIFY(is_any()); _dup(); return boost::get<boost::any>(storage->data); } template <typename T> T& as_any_lval() { return any_cast<T&>(as_any_lval()); } const boost::any& as_any() const { VERIFY(is_any()); return boost::get<boost::any>(storage->data); } template <typename T> const T& as_any() const { return any_cast<const T&>(as_any()); } void set_any(const boost::any& val) { set_type(ANY); storage->data = val; } template <typename T> void set_any(T& val) { set_type(ANY); storage->data = boost::any(val); } /** * Data conversion methods. These methods convert a value object to * its underlying type, where possible. If not possible, an * exception is thrown. */ bool to_boolean() const; int to_int() const; long to_long() const; std::size_t to_size_t() const { return static_cast<std::size_t>(to_long()); } datetime_t to_datetime() const; date_t to_date() const; amount_t to_amount() const; balance_t to_balance() const; string to_string() const; mask_t to_mask() const; sequence_t to_sequence() const; /** * Dynamic typing conversion methods. * * `cast(type_t)' returns a new value whose type has been cast to * the given type, but whose value is based on the original value. * For example, the uncommoditized AMOUNT "100.00" could be cast to * an INTEGER value. If a cast would lose information or is not * meaningful, an exception is thrown. * * `simplify()' is an automatic cast to the simplest type that can * still represent the original value. * * There are also "in-place" versions of these two methods: * in_place_cast * in_place_simplify */ value_t casted(type_t cast_type) const { value_t temp(*this); temp.in_place_cast(cast_type); return temp; } void in_place_cast(type_t cast_type); value_t simplified() const { value_t temp = *this; temp.in_place_simplify(); return temp; } void in_place_simplify(); value_t number() const; /** * Annotated commodity methods. */ void annotate(const annotation_t& details); bool has_annotation() const; annotation_t& annotation(); const annotation_t& annotation() const { return const_cast<value_t&>(*this).annotation(); } value_t strip_annotations(const keep_details_t& what_to_keep) const; /** * Collection-style access methods for SEQUENCE values. */ value_t& operator[](const std::size_t index) { VERIFY(! is_null()); if (is_sequence()) return as_sequence_lval()[index]; else if (index == 0) return *this; assert(false); static value_t null; return null; } const value_t& operator[](const std::size_t index) const { VERIFY(! is_null()); if (is_sequence()) return as_sequence()[index]; else if (index == 0) return *this; assert(false); static value_t null; return null; } void push_front(const value_t& val) { if (is_null()) *this = sequence_t(); if (! is_sequence()) in_place_cast(SEQUENCE); as_sequence_lval().push_front(new value_t(val)); } void push_back(const value_t& val) { if (is_null()) *this = sequence_t(); if (! is_sequence()) in_place_cast(SEQUENCE); as_sequence_lval().push_back(new value_t(val)); } void pop_back() { VERIFY(! is_null()); if (! is_sequence()) { storage.reset(); } else { as_sequence_lval().pop_back(); const sequence_t& seq(as_sequence()); std::size_t new_size = seq.size(); if (new_size == 0) { storage.reset(); } else if (new_size == 1) { *this = seq.front(); } } } sequence_t::iterator begin() { VERIFY(is_sequence()); return as_sequence_lval().begin(); } sequence_t::iterator end() { VERIFY(is_sequence()); return as_sequence_lval().end(); } sequence_t::const_iterator begin() const { VERIFY(is_sequence()); return as_sequence().begin(); } sequence_t::const_iterator end() const { VERIFY(is_sequence()); return as_sequence().end(); } std::size_t size() const { if (is_null()) return 0; else if (is_sequence()) return as_sequence().size(); else return 1; } bool empty() const { return size() == 0; } /** * Informational methods. */ string label(optional<type_t> the_type = none) const; /** * Printing methods. */ void print(std::ostream& out, const int first_width = -1, const int latter_width = -1, const uint_least8_t flags = AMOUNT_PRINT_NO_FLAGS) const; void dump(std::ostream& out, const bool relaxed = true) const; /** * Debugging methods. */ bool valid() const; }; #define NULL_VALUE (value_t()) inline value_t string_value(const string& str = "") { return value_t(str, true); } #define VALUE_OR_ZERO(val) ((val).is_null() ? value_t(0L) : (val)) #define SIMPLIFIED_VALUE_OR_ZERO(val) \ ((val).is_null() ? value_t(0L) : (val).simplified()) inline value_t mask_value(const string& str) { return value_t(mask_t(str)); } inline std::ostream& operator<<(std::ostream& out, const value_t& val) { val.print(out); return out; } inline string value_context(const value_t& val) { std::ostringstream buf; val.print(buf, 20, 20, true); return buf.str(); } inline value_t scope_value(scope_t * val) { return value_t(val); } template <typename T> inline value_t& add_or_set_value(value_t& lhs, const T& rhs) { if (lhs.is_null()) lhs = rhs; else lhs += rhs; return lhs; } struct sort_value_t { bool inverted; value_t value; sort_value_t() : inverted(false) {} }; bool sort_value_is_less_than(const std::list<sort_value_t>& left_values, const std::list<sort_value_t>& right_values); void put_value(property_tree::ptree& pt, const value_t& value); } // namespace ledger #endif // INCLUDED_VALUE_H �������������ledger-3.3.2/src/views.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000013302�14411236400�0015206�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if DOCUMENT_MODEL #include <system.hh> #include "views.h" #include "report.h" #include "journal.h" #include "xact.h" #include "post.h" #include "account.h" namespace ledger { r_xact_ptr r_journal_t::create_xact(xact_t * xact) { r_xact_ptr x = new r_xact_t(this, xact); add_xact(x); assert(xact->data == NULL); xact->data = &x; return x; } void r_journal_t::add_xact(r_xact_ptr xact) { xacts.push_back(xact); } r_post_ptr r_journal_t::add_post(post_t * post) { r_xact_ptr x; if (post->xact->data) x = *static_cast<r_xact_ptr *>(post->xact->data); else x = create_xact(post->xact); r_post_ptr p = create_post(post, x, create_account(post->account)); return p; } void r_journal_t::add_post(r_post_ptr post) { posts.push_back(post); } r_post_ptr r_journal_t::create_post(post_t * post, r_xact_ptr xact, r_account_ptr account) { r_post_ptr p = new r_post_t(this, post, xact, account); add_post(p); xact->add_post(p); account->add_post(p); return p; } r_post_ptr r_journal_t::create_post(r_post_ptr post, r_xact_ptr xact, r_account_ptr account) { r_post_ptr temp(new r_post_t(*post.get())); add_post(temp); temp->set_xact(xact); xact->add_post(temp); temp->set_account(account); account->add_post(temp); return temp; } r_account_ptr r_journal_t::create_account(account_t * account) { return create_account(account->fullname()); } r_account_ptr r_journal_t::create_account(const std::string& name) { return master_ptr->create_account(name); } const optional<position_t> r_item_t::position() const { return ptr()->pos; } date_t r_item_t::date() const { return ptr()->date(); } void r_item_t::set_date(const date_t& when) { } item_t::state_t r_item_t::state() const { return ptr()->state(); } void r_item_t::set_state(item_t::state_t val) { } string r_item_t::payee() const { if (optional<value_t> desc = get_tag(_("Payee"))) return desc->as_string(); else return empty_string; } void r_item_t::set_payee(const string& name) { } void r_item_t::define(const symbol_t::kind_t, const string& name, expr_t::ptr_op_t def) { bind_scope_t bound_scope(*scope_t::default_scope, *this); set_tag(name, def->calc(bound_scope)); } expr_t::ptr_op_t r_item_t::lookup(const symbol_t::kind_t kind, const string& name) { if (kind != symbol_t::FUNCTION) return NULL; switch (name[0]) { } return base_item->lookup(kind, name); } string r_xact_t::description() { return ptr()->description(); } void r_xact_t::add_post(r_post_ptr post) { posts.push_back(post); } string r_xact_t::payee() const { string desc(r_item_t::payee()); if (desc.empty()) return ptr()->payee; else return desc; } string r_post_t::description() { return ptr()->description(); } string r_post_t::payee() const { string desc(r_item_t::payee()); if (desc.empty()) return const_cast<r_post_t *>(this)->xact()->payee(); else return desc; } string r_account_t::description() { return string(_("account ")) + fullname(); } void r_account_t::add_post(r_post_ptr post) { posts.push_back(post); } r_account_ptr r_account_t::create_account(const std::string& fname) { string::size_type sep = fname.find(':'); string head, tail; if (sep == string::npos) { head = fname; } else { head = string(fname, 0, sep); tail = string(fname, sep + 1); } std::pair<r_accounts_map::iterator, bool> result = accounts.insert(r_accounts_map::value_type (head, new r_account_t(journal_ptr, this, name))); r_account_ptr acct((*result.first).second); if (tail.empty()) return acct; else return acct->create_account(tail); } string r_account_t::fullname() const { if (! _fullname.empty()) { return _fullname; } else { r_account_ptr first = NULL; string fname = name; while (! first || first->parent_ptr) { first = first ? first->parent_ptr : parent_ptr; if (! first->name.empty()) fname = first->name + ":" + fname; } _fullname = fname; return fname; } } } // namespace ledger #endif /* DOCUMENT_MODEL */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/views.h����������������������������������������������������������������������������0000664�0000000�0000000�00000026453�14411236400�0015063�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup views */ /** * @file views.h * @author John Wiegley * * @ingroup views */ #ifndef INCLUDED_VIEWS_H #define INCLUDED_VIEWS_H #include "utils.h" #if DOCUMENT_MODEL #include "scope.h" #include "item.h" #include "report.h" #include "post.h" #include "predicate.h" namespace ledger { class journal_t; class xact_t; class post_t; class account_t; class report_t; class r_base_t : public supports_flags<uint_least16_t>, public scope_t { public: r_base_t() : refc(0) { TRACE_CTOR(r_base_t, ""); } r_base_t(const r_base_t& other) : refc(0) { TRACE_CTOR(r_base_t, "copy"); } virtual ~r_base_t() { TRACE_DTOR(r_base_t); } protected: /** * `refc' holds the current reference count for each object. */ mutable int refc; /** * Reference counting methods. The intrusive_ptr_* methods are used * by boost::intrusive_ptr to manage the calls to acquire and release. */ void acquire() const { VERIFY(refc >= 0); refc++; } void release() const { VERIFY(refc > 0); if (--refc == 0) checked_delete(this); } friend inline void intrusive_ptr_add_ref(r_base_t * r_ptr) { r_ptr->acquire(); } friend inline void intrusive_ptr_release(r_base_t * r_ptr) { r_ptr->release(); } }; class r_journal_t; class r_item_t; class r_xact_t; class r_post_t; class r_account_t; typedef intrusive_ptr<r_journal_t> r_journal_ptr; typedef intrusive_ptr<r_item_t> r_item_ptr; typedef intrusive_ptr<r_xact_t> r_xact_ptr; typedef intrusive_ptr<r_post_t> r_post_ptr; typedef intrusive_ptr<r_account_t> r_account_ptr; typedef std::list<r_xact_ptr> r_xacts_list; typedef std::list<r_post_ptr> r_posts_list; class r_journal_t : public r_base_t { journal_t * base_journal; journal_t * ptr() { return base_journal; } const journal_t * ptr() const { return base_journal; } r_account_ptr master_ptr; r_xacts_list xacts; r_posts_list posts; void set_master(r_account_ptr ptr) { master_ptr = ptr; } public: r_journal_t(journal_t * _journal, r_account_ptr _master) : r_base_t(), base_journal(_journal), master_ptr(_master) { TRACE_CTOR(r_journal_t, "journal_t *, account_t *"); } r_journal_t(const r_journal_t& other) : r_base_t(other), base_journal(other.base_journal), master_ptr(other.master_ptr), xacts(other.xacts), posts(other.posts) { TRACE_CTOR(r_journal_t, "copy"); } virtual ~r_journal_t() { TRACE_DTOR(r_journal_t); } r_xact_ptr create_xact(xact_t * xact = NULL); void add_xact(r_xact_ptr xact); r_xacts_list::iterator xacts_begin() { return xacts.begin(); } r_xacts_list::iterator xacts_end() { return xacts.end(); } r_post_ptr add_post(post_t * post); void add_post(r_post_ptr post); r_post_ptr create_post(post_t * post = NULL, r_xact_ptr xact = NULL, r_account_ptr account = NULL); r_post_ptr create_post(r_post_ptr post, r_xact_ptr xact = NULL, r_account_ptr account = NULL); r_posts_list::iterator posts_begin() { return posts.begin(); } r_posts_list::iterator posts_end() { return posts.end(); } r_account_ptr create_account(account_t * account = NULL); r_account_ptr create_account(const std::string& name); friend void put_journal(property_tree::ptree& pt, r_journal_ptr journal); }; class r_item_t : public r_base_t { protected: item_t * base_item; item_t * ptr() { return base_item; } const item_t * ptr() const { return base_item; } r_journal_ptr journal_ptr; public: r_item_t(r_journal_ptr _journal_ptr, item_t * _item) : r_base_t(), base_item(_item), journal_ptr(_journal_ptr) { TRACE_CTOR(r_item_t, "r_journal_ptr, item_t *"); } r_item_t(const r_item_t& other) : r_base_t(other), base_item(other.base_item), journal_ptr(other.journal_ptr) { TRACE_CTOR(r_item_t, "copy"); } virtual ~r_item_t() { TRACE_DTOR(r_item_t); } const optional<position_t> position() const; string id() const { return ptr()->id(); } std::size_t seq() const { return ptr()->seq(); } date_t date() const; void set_date(const date_t& when); item_t::state_t state() const; void set_state(item_t::state_t val); string payee() const; void set_payee(const string& name); optional<string> note() const { return ptr()->note; } bool has_tag(const string& tag) const { return ptr()->has_tag(tag); } bool has_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask = none) const { return ptr()->has_tag(tag_mask, value_mask); } optional<value_t> get_tag(const string& tag) const { return ptr()->get_tag(tag); } optional<value_t> get_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask = none) const { return ptr()->get_tag(tag_mask, value_mask); } void set_tag(const string& tag, const optional<value_t>& value = none, const bool overwrite_existing = true) { ptr()->set_tag(tag, value, overwrite_existing); } /** * Symbol scope methods. */ virtual void define(const symbol_t::kind_t, const string&, expr_t::ptr_op_t); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); friend class r_journal_t; friend void put_item(property_tree::ptree& pt, r_item_ptr journal); }; class r_xact_t : public r_item_t { xact_t * ptr() { return reinterpret_cast<xact_t *>(base_item); } const xact_t * ptr() const { return reinterpret_cast<const xact_t *>(base_item); } r_posts_list posts; public: r_xact_t(r_journal_ptr journal_ptr, xact_t * _xact) : r_item_t(journal_ptr, reinterpret_cast<item_t *>(_xact)) { TRACE_CTOR(r_xact_t, "r_journal_ptr, xact_t *"); } r_xact_t(const r_xact_t& other) : r_item_t(other), posts(other.posts) { TRACE_CTOR(r_xact_t, "copy"); } virtual ~r_xact_t() { TRACE_DTOR(r_xact_t); } virtual string description(); void add_post(r_post_ptr post); #if 0 r_post_ptr create_post(post_t * post = NULL, r_account_ptr account = NULL); r_post_ptr create_post(r_post_ptr post, r_account_ptr account = NULL); #endif r_posts_list::iterator posts_begin() { return posts.begin(); } r_posts_list::iterator posts_end() { return posts.end(); } string code() const; string payee() const; friend class r_journal_t; friend void put_xact(property_tree::ptree& pt, r_xact_ptr journal); }; class r_post_t : public r_item_t { post_t * ptr() { return reinterpret_cast<post_t *>(base_item); } const post_t * ptr() const { return reinterpret_cast<const post_t *>(base_item); } r_xact_ptr xact_ptr; r_account_ptr account_ptr; void set_xact(r_xact_ptr ptr) { xact_ptr = ptr; } void set_account(r_account_ptr ptr) { account_ptr = ptr; } public: r_post_t(r_journal_ptr journal_ptr, post_t * _post, r_xact_ptr _xact_ptr, r_account_ptr _account_ptr) : r_item_t(journal_ptr, reinterpret_cast<item_t *>(_post)), xact_ptr(_xact_ptr), account_ptr(_account_ptr) { TRACE_CTOR(r_post_t, "r_journal_ptr, post_t *, r_xact_ptr, r_account_ptr"); } r_post_t(const r_post_t& other) : r_item_t(other), xact_ptr(other.xact_ptr), account_ptr(other.account_ptr) { TRACE_CTOR(r_post_t, "copy"); } virtual ~r_post_t() { TRACE_DTOR(r_post_t); } virtual string description(); string payee() const; r_xact_ptr xact(); r_account_ptr account(); value_t amount() const; value_t cost() const; std::size_t count() const; value_t running_total() const; optional<datetime_t> checkin() const; optional<datetime_t> checkout() const; friend class r_journal_t; friend void put_post(property_tree::ptree& pt, r_post_ptr journal); }; typedef std::map<string, r_account_ptr> r_accounts_map; class r_account_t : public r_base_t { r_journal_ptr journal_ptr; r_account_ptr parent_ptr; r_accounts_map accounts; r_posts_list posts; string name; mutable string _fullname; public: r_account_t(r_journal_ptr _journal_ptr, r_account_ptr _parent_ptr, string _name) : r_base_t(), journal_ptr(_journal_ptr), parent_ptr(_parent_ptr), name(_name) { TRACE_CTOR(r_account_t, "r_journal_ptr, r_account_ptr, string"); } r_account_t(const r_account_t& other) : r_base_t(other), journal_ptr(other.journal_ptr), parent_ptr(other.parent_ptr), accounts(other.accounts), posts(other.posts), name(other.name), _fullname(other._fullname) { TRACE_CTOR(r_account_t, "copy"); } virtual ~r_account_t() { TRACE_DTOR(r_account_t); } virtual string description(); void add_post(r_post_ptr post); r_posts_list::iterator posts_begin() { return posts.begin(); } r_posts_list::iterator posts_end() { return posts.end(); } r_account_ptr create_account(const std::string& name); string fullname() const; /** * Symbol scope methods. */ virtual void define(const symbol_t::kind_t, const string&, expr_t::ptr_op_t) {} virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& fname) { return NULL; } friend class r_journal_t; friend void put_account(property_tree::ptree& pt, r_account_ptr journal); }; template <typename PostsIterator> void populate_journal(r_journal_ptr journal, report_t& report, PostsIterator iter, predicate_t& pred) { while (post_t * post = *iter) { bind_scope_t bound_scope(report, *post); if (pred.calc(bound_scope)) journal->add_post(post); iter.increment(); } } } // namespace ledger #endif /* DOCUMENT_MODEL */ #endif // INCLUDED_VIEWS_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/wcwidth.c.patch��������������������������������������������������������������������0000664�0000000�0000000�00000003335�14411236400�0016462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- wcwidth.c 2007-05-26 18:06:24.000000000 +0800 +++ wcwidth.cc 2014-02-13 18:36:18.668331252 +0800 @@ -59,15 +59,23 @@ * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ -#include <wchar.h> +/* This file is modified to work with C++ and present Unicode + * characters in uint32_t type. + */ + +#include <system.hh> + +namespace ledger { -struct interval { - int first; - int last; -}; +namespace { + struct interval { + int first; + int last; + }; +} /* auxiliary function for binary search in interval table */ -static int bisearch(wchar_t ucs, const struct interval *table, int max) { +static int bisearch(boost::uint32_t ucs, const struct interval *table, int max) { int min = 0; int mid; @@ -119,7 +127,7 @@ * in ISO 10646. */ -int mk_wcwidth(wchar_t ucs) +int mk_wcwidth(boost::uint32_t ucs) { /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ @@ -204,7 +212,7 @@ } -int mk_wcswidth(const wchar_t *pwcs, size_t n) +int mk_wcswidth(const boost::uint32_t *pwcs, size_t n) { int w, width = 0; @@ -227,7 +235,7 @@ * the traditional terminal character-width behaviour. It is not * otherwise recommended for general use. */ -int mk_wcwidth_cjk(wchar_t ucs) +int mk_wcwidth_cjk(boost::uint32_t ucs) { /* sorted list of non-overlapping intervals of East Asian Ambiguous * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ @@ -295,7 +303,7 @@ } -int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) +int mk_wcswidth_cjk(const boost::uint32_t *pwcs, size_t n) { int w, width = 0; @@ -307,3 +315,5 @@ return width; } + +} // namespace ledger ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/wcwidth.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000033534�14411236400�0015533�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * This is an implementation of wcwidth() and wcswidth() (defined in * IEEE Std 1002.1-2001) for Unicode. * * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html * * In fixed-width output devices, Latin characters all occupy a single * "cell" position of equal width, whereas ideographic CJK characters * occupy two such cells. Interoperability between terminal-line * applications and (teletype-style) character terminals using the * UTF-8 encoding requires agreement on which character should advance * the cursor by how many cell positions. No established formal * standards exist at present on which Unicode character shall occupy * how many cell positions on character terminals. These routines are * a first attempt of defining such behavior based on simple rules * applied to data provided by the Unicode Consortium. * * For some graphical characters, the Unicode standard explicitly * defines a character-cell width via the definition of the East Asian * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. * In all these cases, there is no ambiguity about which width a * terminal shall use. For characters in the East Asian Ambiguous (A) * class, the width choice depends purely on a preference of backward * compatibility with either historic CJK or Western practice. * Choosing single-width for these characters is easy to justify as * the appropriate long-term solution, as the CJK practice of * displaying these characters as double-width comes from historic * implementation simplicity (8-bit encoded characters were displayed * single-width and 16-bit ones double-width, even for Greek, * Cyrillic, etc.) and not any typographic considerations. * * Much less clear is the choice of width for the Not East Asian * (Neutral) class. Existing practice does not dictate a width for any * of these characters. It would nevertheless make sense * typographically to allocate two character cells to characters such * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be * represented adequately with a single-width glyph. The following * routines at present merely assign a single-cell width to all * neutral characters, in the interest of simplicity. This is not * entirely satisfactory and should be reconsidered before * establishing a formal standard in this area. At the moment, the * decision which Not East Asian (Neutral) characters should be * represented by double-width glyphs cannot yet be answered by * applying a simple rule from the Unicode database content. Setting * up a proper standard for the behavior of UTF-8 character terminals * will require a careful analysis not only of each Unicode character, * but also of each presentation form, something the author of these * routines has avoided to do so far. * * http://www.unicode.org/unicode/reports/tr11/ * * Markus Kuhn -- 2007-05-26 (Unicode 5.0) * * Permission to use, copy, modify, and distribute this software * for any purpose and without fee is hereby granted. The author * disclaims all warranties with regard to this software. * * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ /* This file is modified to work with C++ and present Unicode * characters in uint32_t type. */ #include <system.hh> namespace ledger { namespace { struct interval { boost::uint32_t first; boost::uint32_t last; }; } /* auxiliary function for binary search in interval table */ static int bisearch(boost::uint32_t ucs, const struct interval *table, int max) { int min = 0; int mid; if (ucs < table[0].first || ucs > table[max].last) return 0; while (max >= min) { mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; else if (ucs < table[mid].first) max = mid - 1; else return 1; } return 0; } /* The following two functions define the column width of an ISO 10646 * character as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return * value of -1. * * - Non-spacing and enclosing combining characters (general * category code Mn or Me in the Unicode database) have a * column width of 0. * * - SOFT HYPHEN (U+00AD) has a column width of 1. * * - Other format characters (general category code Cf in the Unicode * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) * have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian * Full-width (F) category as defined in Unicode Technical * Report #11 have a column width of 2. * * - All remaining characters (including all printable * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ int mk_wcwidth(boost::uint32_t ucs) { /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static const struct interval combining[] = { { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF } }; /* test for 8-bit control characters */ if (ucs == 0) return 0; if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; /* binary search in table of non-spacing characters */ if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ return 1 + (ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ ucs == 0x2329 || ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd))); } int mk_wcswidth(const boost::uint32_t *pwcs, size_t n) { int w, width = 0; for (;*pwcs && n-- > 0; pwcs++) if ((w = mk_wcwidth(*pwcs)) < 0) return -1; else width += w; return width; } /* * The following functions are the same as mk_wcwidth() and * mk_wcswidth(), except that spacing characters in the East Asian * Ambiguous (A) category as defined in Unicode Technical Report #11 * have a column width of 2. This variant might be useful for users of * CJK legacy encodings who want to migrate to UCS without changing * the traditional terminal character-width behaviour. It is not * otherwise recommended for general use. */ int mk_wcwidth_cjk(boost::uint32_t ucs) { /* sorted list of non-overlapping intervals of East Asian Ambiguous * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ static const struct interval ambiguous[] = { { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } }; /* binary search in table of non-spacing characters */ if (bisearch(ucs, ambiguous, sizeof(ambiguous) / sizeof(struct interval) - 1)) return 2; return mk_wcwidth(ucs); } int mk_wcswidth_cjk(const boost::uint32_t *pwcs, size_t n) { int w, width = 0; for (;*pwcs && n-- > 0; pwcs++) if ((w = mk_wcwidth_cjk(*pwcs)) < 0) return -1; else width += w; return width; } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/xact.cc����������������������������������������������������������������������������0000664�0000000�0000000�00000064306�14411236400�0015022�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <system.hh> #include "xact.h" #include "post.h" #include "account.h" #include "journal.h" #include "context.h" #include "format.h" #include "pool.h" namespace ledger { xact_base_t::xact_base_t(const xact_base_t& xact_base) : item_t(xact_base), journal(xact_base.journal) { TRACE_CTOR(xact_base_t, "copy"); } xact_base_t::~xact_base_t() { TRACE_DTOR(xact_base_t); if (! has_flags(ITEM_TEMP)) { foreach (post_t * post, posts) { // If the posting is a temporary, it will be destructed when the // temporary is. assert(! post->has_flags(ITEM_TEMP)); if (post->account) post->account->remove_post(post); checked_delete(post); } } } void xact_base_t::add_post(post_t * post) { #if !NO_ASSERTS // You can add temporary postings to transactions, but not real postings to // temporary transactions. if (! post->has_flags(ITEM_TEMP)) assert(! has_flags(ITEM_TEMP)); #endif posts.push_back(post); } bool xact_base_t::remove_post(post_t * post) { posts.remove(post); post->xact = NULL; return true; } bool xact_base_t::has_xdata() { foreach (post_t * post, posts) if (post->has_xdata()) return true; return false; } void xact_base_t::clear_xdata() { foreach (post_t * post, posts) if (! post->has_flags(ITEM_TEMP)) post->clear_xdata(); } value_t xact_base_t::magnitude() const { value_t halfbal = 0L; foreach (const post_t * post, posts) { if (post->amount.sign() > 0) { if (post->cost) halfbal += *post->cost; else halfbal += post->amount; } } return halfbal; } namespace { inline bool account_ends_with_special_char(const string& name) { string::size_type len(name.length()); return (std::isdigit(name[len - 1]) || name[len - 1] == ')' || name[len - 1] == '}' || name[len - 1] == ']'); } struct add_balancing_post { bool first; xact_base_t& xact; post_t * null_post; explicit add_balancing_post(xact_base_t& _xact, post_t * _null_post) : first(true), xact(_xact), null_post(_null_post) { TRACE_CTOR(add_balancing_post, "xact_base_t&, post_t *"); } add_balancing_post(const add_balancing_post& other) : first(other.first), xact(other.xact), null_post(other.null_post) { TRACE_CTOR(add_balancing_post, "copy"); } ~add_balancing_post() throw() { TRACE_DTOR(add_balancing_post); } void operator()(const amount_t& amount) { if (first) { null_post->amount = amount.negated(); null_post->add_flags(POST_CALCULATED); first = false; } else { unique_ptr<post_t> p(new post_t(null_post->account, amount.negated(), null_post->flags() | ITEM_GENERATED | POST_CALCULATED)); p->set_state(null_post->state()); xact.add_post(p.release()); } } }; } bool xact_base_t::finalize() { // Scan through and compute the total balance for the xact. This is used // for auto-calculating the value of xacts with no cost, and the per-unit // price of unpriced commodities. value_t balance; post_t * null_post = NULL; foreach (post_t * post, posts) { if (! post->must_balance()) continue; amount_t& p(post->cost ? *post->cost : post->amount); if (! p.is_null()) { DEBUG("xact.finalize", "post must balance = " << p.reduced()); // If the amount was a cost, it very likely has the // "keep_precision" flag set, meaning commodity display precision // is ignored when displaying the amount. We never want this set // for the balance, so we must clear the flag in a temporary to // avoid it propagating into the balance. add_or_set_value(balance, p.keep_precision() ? p.rounded().reduced() : p.reduced()); } else if (null_post) { bool post_account_bad = account_ends_with_special_char(post->account->fullname()); bool null_post_account_bad = account_ends_with_special_char(null_post->account->fullname()); if (post_account_bad || null_post_account_bad) throw_(std::logic_error, _f("Posting with null amount's account may be misspelled:\n \"%1%\"") % (post_account_bad ? post->account->fullname() : null_post->account->fullname())); else throw_(std::logic_error, _("Only one posting with null amount allowed per transaction")); } else { null_post = post; } } VERIFY(balance.valid()); #if DEBUG_ON DEBUG("xact.finalize", "initial balance = " << balance); DEBUG("xact.finalize", "balance is " << balance.label()); if (balance.is_balance()) DEBUG("xact.finalize", "balance commodity count = " << balance.as_balance().amounts.size()); #endif // If there is only one post, balance against the default account if one has // been set. if (journal && journal->bucket && posts.size() == 1 && ! balance.is_null()) { null_post = new post_t(journal->bucket, ITEM_GENERATED); null_post->_state = (*posts.begin())->_state; add_post(null_post); } if (! null_post && balance.is_balance() && balance.as_balance().amounts.size() == 2) { // When an xact involves two different commodities (regardless of how // many posts there are) determine the conversion ratio by dividing the // total value of one commodity by the total value of the other. This // establishes the per-unit cost for this post for both commodities. DEBUG("xact.finalize", "there were exactly two commodities, and no null post"); bool saw_cost = false; post_t * top_post = NULL; foreach (post_t * post, posts) { if (! post->amount.is_null() && post->must_balance()) { if (post->amount.has_annotation()) top_post = post; else if (! top_post) top_post = post; } if (post->cost && ! post->has_flags(POST_COST_CALCULATED)) { saw_cost = true; break; } } if (! saw_cost && top_post) { const balance_t& bal(balance.as_balance()); DEBUG("xact.finalize", "there were no costs, and a valid top_post"); balance_t::amounts_map::const_iterator a = bal.amounts.begin(); const amount_t * x = &(*a++).second; const amount_t * y = &(*a++).second; if (*x && *y) { if (x->commodity() != top_post->amount.commodity()) std::swap(x, y); DEBUG("xact.finalize", "primary amount = " << *x); DEBUG("xact.finalize", "secondary amount = " << *y); commodity_t& comm(x->commodity()); amount_t per_unit_cost = (*y / *x).abs().unrounded(); DEBUG("xact.finalize", "per_unit_cost = " << per_unit_cost); foreach (post_t * post, posts) { const amount_t& amt(post->amount.reduced()); if (post->must_balance() && amt.commodity() == comm) { balance -= amt; post->cost = per_unit_cost * amt; post->add_flags(POST_COST_CALCULATED); balance += *post->cost; DEBUG("xact.finalize", "set post->cost to = " << *post->cost); } } } } } posts_list copy(posts); if (has_date()) { foreach (post_t * post, copy) { if (! post->cost) continue; if (post->amount.commodity() == post->cost->commodity()) throw_(balance_error, _("A posting's cost must be of a different commodity than its amount")); cost_breakdown_t breakdown = commodity_pool_t::current_pool->exchange( post->amount, *post->cost, false, ! post->has_flags(POST_COST_VIRTUAL), datetime_t(date(), time_duration(0, 0, 0, 0))); if (post->amount.has_annotation() && post->amount.annotation().price) { if (breakdown.basis_cost.commodity() == breakdown.final_cost.commodity()) { DEBUG("xact.finalize", "breakdown.basis_cost = " << breakdown.basis_cost); DEBUG("xact.finalize", "breakdown.final_cost = " << breakdown.final_cost); if (amount_t gain_loss = breakdown.basis_cost - breakdown.final_cost) { DEBUG("xact.finalize", "gain_loss = " << gain_loss); gain_loss.in_place_round(); DEBUG("xact.finalize", "gain_loss rounds to = " << gain_loss); if (post->must_balance()) add_or_set_value(balance, gain_loss.reduced()); #if 0 account_t * account; if (gain_loss.sign() > 0) account = journal->find_account(_("Equity:Capital Gains")); else account = journal->find_account(_("Equity:Capital Losses")); post_t * p = new post_t(account, gain_loss, ITEM_GENERATED); p->set_state(post->state()); if (post->has_flags(POST_VIRTUAL)) { DEBUG("xact.finalize", "gain_loss came from a virtual post"); p->add_flags(post->flags() & (POST_VIRTUAL | POST_MUST_BALANCE)); } add_post(p); #else *post->cost += gain_loss; #endif DEBUG("xact.finalize", "added gain_loss, balance = " << balance); } else { DEBUG("xact.finalize", "gain_loss would have displayed as zero"); } } } else { post->amount = breakdown.amount.has_annotation() ? amount_t(breakdown.amount, annotation_t(breakdown.amount.annotation().price, breakdown.amount.annotation().date, post->amount.has_annotation() ? post->amount.annotation().tag : breakdown.amount.annotation().tag, breakdown.amount.annotation().value_expr)) : breakdown.amount; DEBUG("xact.finalize", "added breakdown, balance = " << balance); } if (post->has_flags(POST_COST_FIXATED) && post->amount.has_annotation() && post->amount.annotation().price) { DEBUG("xact.finalize", "fixating annotation price"); post->amount.annotation().add_flags(ANNOTATION_PRICE_FIXATED); } } } if (null_post != NULL) { // If one post has no value at all, its value will become the inverse of // the rest. If multiple commodities are involved, multiple posts are // generated to balance them all. DEBUG("xact.finalize", "there was a null posting"); add_balancing_post post_adder(*this, null_post); if (balance.is_balance()) balance.as_balance_lval().map_sorted_amounts(post_adder); else if (balance.is_amount()) post_adder(balance.as_amount_lval()); else if (balance.is_long()) post_adder(balance.to_amount()); else if (! balance.is_null() && ! balance.is_realzero()) throw_(balance_error, _("Transaction does not balance")); balance = NULL_VALUE; } DEBUG("xact.finalize", "resolved balance = " << balance); if (! balance.is_null() && ! balance.is_zero()) { add_error_context(item_context(*this, _("While balancing transaction"))); add_error_context(_("Unbalanced remainder is:")); add_error_context(value_context(balance)); add_error_context(_("Amount to balance against:")); add_error_context(value_context(magnitude())); throw_(balance_error, _("Transaction does not balance")); } // Add a pointer to each posting to their related accounts if (dynamic_cast<xact_t *>(this)) { bool all_null = true; bool some_null = false; foreach (post_t * post, posts) { assert(post->account); if (! post->amount.is_null()) { all_null = false; post->amount.in_place_reduce(); } else { some_null = true; } if (post->has_flags(POST_DEFERRED)) { if (!post->amount.is_null()) post->account->add_deferred_post(id(), post); } else { post->account->add_post(post); } post->xdata().add_flags(POST_EXT_VISITED); post->account->xdata().add_flags(ACCOUNT_EXT_VISITED); } if (all_null) return false; // ignore this xact completely else if (some_null) throw_(balance_error, _("There cannot be null amounts after balancing a transaction")); } VERIFY(valid()); return true; } bool xact_base_t::verify() { // Scan through and compute the total balance for the xact. value_t balance; foreach (post_t * post, posts) { if (! post->must_balance()) continue; amount_t& p(post->cost ? *post->cost : post->amount); assert(! p.is_null()); // If the amount was a cost, it very likely has the "keep_precision" flag // set, meaning commodity display precision is ignored when displaying the // amount. We never want this set for the balance, so we must clear the // flag in a temporary to avoid it propagating into the balance. add_or_set_value(balance, p.keep_precision() ? p.rounded().reduced() : p.reduced()); } VERIFY(balance.valid()); // Now that the post list has its final form, calculate the balance once // more in terms of total cost, accounting for any possible gain/loss // amounts. foreach (post_t * post, posts) { if (! post->cost) continue; if (post->amount.commodity() == post->cost->commodity()) throw_(amount_error, _("A posting's cost must be of a different commodity than its amount")); } if (! balance.is_null() && ! balance.is_zero()) { add_error_context(item_context(*this, _("While balancing transaction"))); add_error_context(_("Unbalanced remainder is:")); add_error_context(value_context(balance)); add_error_context(_("Amount to balance against:")); add_error_context(value_context(magnitude())); throw_(balance_error, _("Transaction does not balance")); } VERIFY(valid()); return true; } xact_t::xact_t(const xact_t& e) : xact_base_t(e), code(e.code), payee(e.payee) #if DOCUMENT_MODEL , data(NULL) #endif { TRACE_CTOR(xact_t, "copy"); } void xact_t::add_post(post_t * post) { post->xact = this; xact_base_t::add_post(post); } namespace { value_t get_magnitude(xact_t& xact) { return xact.magnitude(); } value_t get_code(xact_t& xact) { if (xact.code) return string_value(*xact.code); else return NULL_VALUE; } value_t get_payee(xact_t& xact) { return string_value(xact.payee); } template <value_t (*Func)(xact_t&)> value_t get_wrapper(call_scope_t& scope) { return (*Func)(find_scope<xact_t>(scope)); } value_t fn_any(call_scope_t& args) { post_t& post(args.context<post_t>()); expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(0)); foreach (post_t * p, post.xact->posts) { bind_scope_t bound_scope(args, *p); if (expr->calc(bound_scope, args.locus, args.depth).to_boolean()) return true; } return false; } value_t fn_all(call_scope_t& args) { post_t& post(args.context<post_t>()); expr_t::ptr_op_t expr(args.get<expr_t::ptr_op_t>(0)); foreach (post_t * p, post.xact->posts) { bind_scope_t bound_scope(args, *p); if (! expr->calc(bound_scope, args.locus, args.depth).to_boolean()) return false; } return true; } } expr_t::ptr_op_t xact_t::lookup(const symbol_t::kind_t kind, const string& name) { if (kind != symbol_t::FUNCTION) return item_t::lookup(kind, name); switch (name[0]) { case 'a': if (name == "any") return WRAP_FUNCTOR(&fn_any); else if (name == "all") return WRAP_FUNCTOR(&fn_all); break; case 'c': if (name == "code") return WRAP_FUNCTOR(get_wrapper<&get_code>); break; case 'm': if (name == "magnitude") return WRAP_FUNCTOR(get_wrapper<&get_magnitude>); break; case 'p': if (name[1] == '\0' || name == "payee") return WRAP_FUNCTOR(get_wrapper<&get_payee>); break; } return item_t::lookup(kind, name); } bool xact_t::valid() const { if (! _date) { DEBUG("ledger.validate", "xact_t: ! _date"); return false; } foreach (post_t * post, posts) if (post->xact != this || ! post->valid()) { DEBUG("ledger.validate", "xact_t: post not valid"); return false; } return true; } namespace { bool post_pred(expr_t::ptr_op_t op, post_t& post) { switch (op->kind) { case expr_t::op_t::VALUE: return op->as_value().to_boolean(); case expr_t::op_t::O_MATCH: if (op->left()->kind == expr_t::op_t::IDENT && op->left()->as_ident() == "account" && op->right()->kind == expr_t::op_t::VALUE && op->right()->as_value().is_mask()) return op->right()->as_value().as_mask() .match(post.reported_account()->fullname()); else break; case expr_t::op_t::O_EQ: return post_pred(op->left(), post) == post_pred(op->right(), post); case expr_t::op_t::O_NOT: return ! post_pred(op->left(), post); case expr_t::op_t::O_AND: return post_pred(op->left(), post) && post_pred(op->right(), post); case expr_t::op_t::O_OR: return post_pred(op->left(), post) || post_pred(op->right(), post); case expr_t::op_t::O_QUERY: if (post_pred(op->left(), post)) return post_pred(op->right()->left(), post); else return post_pred(op->right()->right(), post); default: break; } throw_(calc_error, _("Unhandled operator")); return false; } } static string apply_format(const string& str, scope_t& scope) { if (contains(str, "%(")) { format_t str_format(str); std::ostringstream buf; buf << str_format(scope); return buf.str(); } else { return str; } } void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) { posts_list initial_posts(xact.posts.begin(), xact.posts.end()); try { bool needs_further_verification = false; foreach (post_t * initial_post, initial_posts) { if (initial_post->has_flags(ITEM_GENERATED)) continue; bind_scope_t bound_scope(*scope_t::default_scope, *initial_post); bool matches_predicate = false; if (try_quick_match) { try { bool found_memoized_result = false; if (! memoized_results.empty()) { std::map<string, bool>::iterator i = memoized_results.find(initial_post->account->fullname()); if (i != memoized_results.end()) { found_memoized_result = true; matches_predicate = (*i).second; } } // Since the majority of people who use automated transactions simply // match against account names, try using a *much* faster version of // the predicate evaluator. if (! found_memoized_result) { matches_predicate = post_pred(predicate.get_op(), *initial_post); memoized_results.insert (std::pair<string, bool>(initial_post->account->fullname(), matches_predicate)); } } catch (...) { DEBUG("xact.extend.fail", "The quick matcher failed, going back to regular eval"); try_quick_match = false; matches_predicate = predicate(bound_scope); } } else { matches_predicate = predicate(bound_scope); } if (matches_predicate) { if (deferred_notes) { foreach (deferred_tag_data_t& data, *deferred_notes) { if (data.apply_to_post == NULL) initial_post->append_note( apply_format(data.tag_data, bound_scope).c_str(), bound_scope, data.overwrite_existing); } } if (check_exprs) { foreach (expr_t::check_expr_pair& pair, *check_exprs) { if (pair.second == expr_t::EXPR_GENERAL) { pair.first.calc(bound_scope); } else if (! pair.first.calc(bound_scope).to_boolean()) { if (pair.second == expr_t::EXPR_ASSERTION) throw_(parse_error, _f("Transaction assertion failed: %1%") % pair.first); else context.warning(_f("Transaction check failed: %1%") % pair.first); } } } foreach (post_t * post, posts) { amount_t post_amount; if (post->amount.is_null()) { if (! post->amount_expr) throw_(amount_error, _("Automated transaction's posting has no amount")); value_t result(post->amount_expr->calc(bound_scope)); if (result.is_long()) { post_amount = result.to_amount(); } else { if (! result.is_amount()) throw_(amount_error, _("Amount expressions must result in a simple amount")); post_amount = result.as_amount(); } } else { post_amount = post->amount; } amount_t amt; if (! post_amount.commodity()) amt = initial_post->amount * post_amount; else amt = post_amount; #if DEBUG_ON IF_DEBUG("xact.extend") { DEBUG("xact.extend", "Initial post on line " << initial_post->pos->beg_line << ": " << "amount " << initial_post->amount << " (precision " << initial_post->amount.precision() << ")"); if (initial_post->amount.keep_precision()) DEBUG("xact.extend", " precision is kept"); DEBUG("xact.extend", "Posting on line " << post->pos->beg_line << ": " << "amount " << post_amount << ", amt " << amt << " (precision " << post_amount.precision() << " != " << amt.precision() << ")"); if (post_amount.keep_precision()) DEBUG("xact.extend", " precision is kept"); if (amt.keep_precision()) DEBUG("xact.extend", " amt precision is kept"); } #endif // DEBUG_ON account_t * account = post->account; string fullname = account->fullname(); assert(! fullname.empty()); if (contains(fullname, "$account")) { fullname = regex_replace(fullname, regex("\\$account\\>"), initial_post->account->fullname()); while (account->parent) account = account->parent; account = account->find_account(fullname); } else if (contains(fullname, "%(")) { format_t account_name(fullname); std::ostringstream buf; buf << account_name(bound_scope); while (account->parent) account = account->parent; account = account->find_account(buf.str()); } // Copy over details so that the resulting post is a mirror of // the automated xact's one. post_t * new_post = new post_t(account, amt); new_post->copy_details(*post); // A Cleared transaction implies all of its automatic posting are cleared // CPR 2012/10/23 if (xact.state() == item_t::CLEARED) { DEBUG("xact.extend.cleared", "CLEARED"); new_post->set_state(item_t::CLEARED); } new_post->add_flags(ITEM_GENERATED); new_post->account = journal->register_account(account->fullname(), new_post, journal->master); if (deferred_notes) { foreach (deferred_tag_data_t& data, *deferred_notes) { if (! data.apply_to_post || data.apply_to_post == post) { new_post->append_note( apply_format(data.tag_data, bound_scope).c_str(), bound_scope, data.overwrite_existing); } } } extend_post(*new_post, *journal); xact.add_post(new_post); new_post->account->add_post(new_post); // Add flags so this post updates the account balance new_post->xdata().add_flags(POST_EXT_VISITED); new_post->account->xdata().add_flags(ACCOUNT_EXT_VISITED); if (new_post->must_balance()) needs_further_verification = true; } } } if (needs_further_verification) xact.verify(); } catch (const std::exception&) { add_error_context(item_context(*this, _("While applying automated transaction"))); add_error_context(item_context(xact, _("While extending transaction"))); throw; } } void put_xact(property_tree::ptree& st, const xact_t& xact) { if (xact.state() == item_t::CLEARED) st.put("<xmlattr>.state", "cleared"); else if (xact.state() == item_t::PENDING) st.put("<xmlattr>.state", "pending"); if (xact.has_flags(ITEM_GENERATED)) st.put("<xmlattr>.generated", "true"); if (xact._date) put_date(st.put("date", ""), *xact._date); if (xact._date_aux) put_date(st.put("aux-date", ""), *xact._date_aux); if (xact.code) st.put("code", *xact.code); st.put("payee", xact.payee); if (xact.note) st.put("note", *xact.note); if (xact.metadata) put_metadata(st.put("metadata", ""), *xact.metadata); } } // namespace ledger ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/src/xact.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000014041�14411236400�0014653�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @addtogroup data */ /** * @file xact.h * @author John Wiegley * * @ingroup data */ #ifndef INCLUDED_XACT_H #define INCLUDED_XACT_H #include "item.h" #include "predicate.h" namespace ledger { class post_t; class journal_t; class parse_context_t; typedef std::list<post_t *> posts_list; class xact_base_t : public item_t { public: journal_t * journal; posts_list posts; xact_base_t() : item_t(), journal(NULL) { TRACE_CTOR(xact_base_t, ""); } xact_base_t(const xact_base_t& e); virtual ~xact_base_t(); virtual void add_post(post_t * post); virtual bool remove_post(post_t * post); posts_list::iterator posts_begin() { return posts.begin(); } posts_list::iterator posts_end() { return posts.end(); } value_t magnitude() const; bool finalize(); bool verify(); bool has_xdata(); void clear_xdata(); virtual bool valid() const { return true; } }; class xact_t : public xact_base_t { public: optional<string> code; string payee; #if DOCUMENT_MODEL mutable void * data; #endif xact_t() #if DOCUMENT_MODEL : data(NULL) #endif { TRACE_CTOR(xact_t, ""); } xact_t(const xact_t& e); virtual ~xact_t() { TRACE_DTOR(xact_t); } virtual string description() { if (pos) { std::ostringstream buf; buf << _f("transaction at line %1%") % pos->beg_line; return buf.str(); } else { return string(_("generated transaction")); } } virtual void add_post(post_t * post); virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); virtual bool valid() const; }; class auto_xact_t : public xact_base_t { public: predicate_t predicate; bool try_quick_match; std::map<string, bool> memoized_results; optional<expr_t::check_expr_list> check_exprs; struct deferred_tag_data_t { string tag_data; bool overwrite_existing; post_t * apply_to_post; deferred_tag_data_t(string _tag_data, bool _overwrite_existing) : tag_data(_tag_data), overwrite_existing(_overwrite_existing), apply_to_post(NULL) {} }; typedef std::list<deferred_tag_data_t> deferred_notes_list; optional<deferred_notes_list> deferred_notes; post_t * active_post; auto_xact_t() : try_quick_match(true), active_post(NULL) { TRACE_CTOR(auto_xact_t, ""); } auto_xact_t(const auto_xact_t& other) : xact_base_t(), predicate(other.predicate), try_quick_match(other.try_quick_match), active_post(other.active_post) { TRACE_CTOR(auto_xact_t, "copy"); } auto_xact_t(const predicate_t& _predicate) : predicate(_predicate), try_quick_match(true), active_post(NULL) { TRACE_CTOR(auto_xact_t, "const predicate_t&"); } virtual ~auto_xact_t() { TRACE_DTOR(auto_xact_t); } virtual string description() { if (pos) { std::ostringstream buf; buf << _f("automated transaction at line %1%") % pos->beg_line; return buf.str(); } else { return string(_("generated automated transaction")); } } virtual void parse_tags(const char * p, scope_t&, bool overwrite_existing = true) { if (! deferred_notes) deferred_notes = deferred_notes_list(); deferred_notes->push_back(deferred_tag_data_t(p, overwrite_existing)); deferred_notes->back().apply_to_post = active_post; } virtual void extend_xact(xact_base_t& xact, parse_context_t& context); }; class period_xact_t : public xact_base_t { public: date_interval_t period; string period_string; period_xact_t() { TRACE_CTOR(period_xact_t, ""); } period_xact_t(const period_xact_t& e) : xact_base_t(e), period(e.period), period_string(e.period_string) { TRACE_CTOR(period_xact_t, "copy"); } period_xact_t(const string& _period) : period(_period), period_string(_period) { TRACE_CTOR(period_xact_t, "const string&"); } virtual ~period_xact_t() { TRACE_DTOR(period_xact_t); } virtual string description() { if (pos) { std::ostringstream buf; buf << _f("periodic transaction at line %1%") % pos->beg_line; return buf.str(); } else { return string(_("generated periodic transaction")); } } }; typedef std::list<xact_t *> xacts_list; typedef std::list<auto_xact_t *> auto_xacts_list; typedef std::list<period_xact_t *> period_xacts_list; void put_xact(property_tree::ptree& pt, const xact_t& xact); } // namespace ledger #endif // INCLUDED_XACT_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/����������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0013733�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/CMakeLists.txt��������������������������������������������������������������������0000664�0000000�0000000�00000005571�14411236400�0016503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(ProcessorCount) ProcessorCount(PROCESSORS) if (NOT PROCESSORS EQUAL 0) math(EXPR JOBS "${PROCESSORS} * 2") set(CTEST_BUILD_FLAGS -j${JOBS}) endif() add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} ${CTEST_BUILD_FLAGS}) add_subdirectory(unit) if (HAVE_BOOST_PYTHON) set(TEST_PYTHON_FLAGS "--python") endif() macro(add_ledger_harness_tests _class) if (Python_EXECUTABLE) file(GLOB ${_class}_TESTS *.test) foreach(TestFile ${${_class}_TESTS}) get_filename_component(TestFile_Name ${TestFile} NAME_WE) string(FIND ${TestFile_Name} "_py" TestFile_IsPythonTest) if ((TestFile_IsPythonTest GREATER -1)) get_filename_component(TestFile_FullName ${TestFile} NAME) string(FIND ${TestFile_FullName} "_py.test" TestFile_IsAnyPythonTest) string(FIND ${TestFile_FullName} "_py${Python_VERSION_MAJOR}.test" TestFile_IsThisPythonTest) if ((TestFile_IsAnyPythonTest EQUAL -1) AND (TestFile_IsThisPythonTest EQUAL -1)) continue() endif() endif() if ((TestFile_IsPythonTest EQUAL -1) OR HAVE_BOOST_PYTHON) add_test(NAME ${_class}Test_${TestFile_Name} COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/RegressTests.py $<TARGET_FILE:ledger> ${PROJECT_SOURCE_DIR} ${TestFile} ${TEST_PYTHON_FLAGS}) set_tests_properties(${_class}Test_${TestFile_Name} PROPERTIES ENVIRONMENT "TZ=${Ledger_TEST_TIMEZONE}") endif() endforeach() endif() endmacro(add_ledger_harness_tests _class) add_subdirectory(manual) add_subdirectory(baseline) add_subdirectory(regress) if (Python_EXECUTABLE) set(_class DocTests) file(GLOB ${_class}_TESTS ${PROJECT_SOURCE_DIR}/doc/*.texi) foreach(TestFile ${${_class}_TESTS}) get_filename_component(TestFile_Name ${TestFile} NAME_WE) add_test(NAME ${_class}Test_${TestFile_Name} COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/${_class}.py --ledger $<TARGET_FILE:ledger> --file ${TestFile}) set_tests_properties(${_class}Test_${TestFile_Name} PROPERTIES ENVIRONMENT "TZ=${Ledger_TEST_TIMEZONE}") endforeach() # CheckManpage and CheckTexinfo are disabled, since they do not work # reliably yet list(APPEND CheckOptions CheckBaselineTests) #CheckManpage CheckTexinfo foreach(_class ${CheckOptions}) add_test(NAME ${_class} COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/${_class}.py --ledger $<TARGET_FILE:ledger> --source ${PROJECT_SOURCE_DIR}) set_tests_properties(${_class} PROPERTIES ENVIRONMENT "TZ=${Ledger_TEST_TIMEZONE}") endforeach() if (HAVE_BOOST_PYTHON) add_test(NAME demo COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/python/demo.py WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) set_tests_properties(demo PROPERTIES ENVIRONMENT "TZ=${Ledger_TEST_TIMEZONE};PYTHONPATH=.") endif() endif() ### CMakeLists.txt ends here ���������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/CheckBaselineTests.py�������������������������������������������������������������0000775�0000000�0000000�00000003707�14411236400�0020022�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import re import os import argparse from os.path import * from subprocess import Popen, PIPE from CheckOptions import CheckOptions class CheckBaselineTests (CheckOptions): def __init__(self, args): CheckOptions.__init__(self, args) self.missing_baseline_tests = set() self.untested_options = [ 'anon', 'args-only', 'debug', 'download', 'force-pager', 'generated', 'help', 'import', 'no-color', 'no-pager', 'options', 'price-exp', 'revalued-total', 'seed', 'trace', 'verbose', 'verify', 'verify-memory', 'version' ] def main(self): for option in self.ledger_options(): if option in self.untested_options: continue baseline_testpath = join(self.source, 'test', 'baseline', 'opt-%s.test' % option) if exists(baseline_testpath) and getsize(baseline_testpath) > 0: continue self.missing_baseline_tests.add(option) if len(self.missing_baseline_tests): print("Missing Baseline test for:%s%s\n" % (self.sep, self.sep.join(sorted(list(self.missing_baseline_tests))))) errors = len(self.missing_baseline_tests) return errors if __name__ == "__main__": def getargs(): parser = argparse.ArgumentParser(prog='CheckBaselineTests', description='Check that ledger options are tested') parser.add_argument('-l', '--ledger', dest='ledger', type=str, action='store', required=True, help='the path to the ledger executable to test with') parser.add_argument('-s', '--source', dest='source', type=str, action='store', required=True, help='the path to the top level ledger source directory') return parser.parse_args() args = getargs() script = CheckBaselineTests(args) status = script.main() sys.exit(status) ���������������������������������������������������������ledger-3.3.2/test/CheckComments.py������������������������������������������������������������������0000664�0000000�0000000�00000007733�14411236400�0017042�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import sys import re import os ok = 100.0 if sys.argv[1] == '-l': ok = float(sys.argv[2]) sys.argv = [sys.argv[0]] + sys.argv[3:] debug = False if sys.argv[1] == '-v': debug = True sys.argv = [sys.argv[0]] + sys.argv[2:] errors = 0 for path in sys.argv[1:]: other_depth = 0 brace_depth = 0 code_count = 0 comment_count = 0 long_comment = None long_comments = {} kind = "function" function_name = "<unknown>" ctor_dtor = False linenum = 0 fd = open(path, 'r') for line in fd.readlines(): linenum += 1 match = re.search('/\*\*(.*)', line) if match: long_comment = re.sub('\s+', '', match.group(1)) continue elif long_comment: match = re.search('(.*)\*/', line) if match: long_comment += re.sub('\s+', '', match.group(1)) comment_count = len(long_comment) long_comment = None else: long_comment += re.sub('\s+', '', line[:-1]) continue if brace_depth == 0: match = re.search('(namespace|enum|class|struct|union)', line) if match: kind = match.group(1) if debug: print "kind =", kind elif kind == "function": match = re.search('(\S+)\(', line) if match: function_name = match.group(1) long_comments[function_name] = comment_count comment_count = 0 if debug: print "name found %s" % function_name if re.search('{', line) and not re.search('@{', line): if kind == "function": brace_depth += 1 if debug: print "brace_depth =", brace_depth else: other_depth += 1 kind = "function" if debug: print "other_depth =", other_depth if re.search('}', line) and not re.search('@}', line): if brace_depth > 0: brace_depth -= 1 if debug: print "brace_depth =", brace_depth if brace_depth == 0: if debug: print "function done" if function_name in long_comments: comment_count += long_comments[function_name] if code_count == 0: percent = ok print "%7s %4d/%4d %s:%d: %s" % \ ("empty", comment_count, code_count, os.path.basename(path), linenum, function_name) errors += 1 else: percent = 100.0 * (float(comment_count) / float(code_count)) if percent < ok and not ctor_dtor: print "%6.0f%% %4d/%4d %s:%d: %s" % \ (percent, comment_count, code_count, os.path.basename(path), linenum, function_name) errors += 1 code_count = 0 comment_count = 0 kind = "function" function_name = "<unknown>" ctor_dtor = False else: other_depth -= 1 if debug: print "other_depth =", other_depth if brace_depth > 0: if re.search("TRACE_[CD]TOR", line): ctor_dtor = True line = re.sub('\s+', '', line[:-1]) match = re.search('//(.*)', line) if match: comment = match.group(1) line = re.sub('//.*', '', line) else: comment = None if line: code_count += len(line) if comment: comment_count += len(comment) sys.exit(errors) �������������������������������������ledger-3.3.2/test/CheckManpage.py�������������������������������������������������������������������0000775�0000000�0000000�00000002250�14411236400�0016615�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import re import os import argparse from os.path import * from subprocess import Popen, PIPE from CheckOptions import CheckOptions class CheckManpage (CheckOptions): def __init__(self, args): CheckOptions.__init__(self, args) self.option_pattern = '\.It Fl \\\\-([-A-Za-z]+)' self.function_pattern = '\.It Fn ([-A-Za-z_]+)' self.source_file = join(self.source, 'doc', 'ledger.1') self.source_type = 'manpage' if __name__ == "__main__": def getargs(): parser = argparse.ArgumentParser(prog='CheckManpage', description='Check that ledger options are documented in the manpage') parser.add_argument('-l', '--ledger', dest='ledger', type=str, action='store', required=True, help='the path to the ledger executable to test with') parser.add_argument('-s', '--source', dest='source', type=str, action='store', required=True, help='the path to the top level ledger source directory') return parser.parse_args() args = getargs() script = CheckManpage(args) status = script.main() sys.exit(status) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/CheckOptions.py�������������������������������������������������������������������0000775�0000000�0000000�00000007415�14411236400�0016710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- import re import os import sys import shlex import argparse import subprocess from os.path import * from subprocess import Popen, PIPE class CheckOptions (object): def __init__(self, args): self.option_pattern = None self.source_file = None self.sep = "\n --" self.ledger = os.path.realpath(args.ledger) self.source = os.path.realpath(args.source) self.missing_options = set() self.unknown_options = set() self.missing_functions = set() self.unknown_functions = set() def find_pattern(self, filename, pattern): regex = re.compile(pattern) return {match.group(1) for match in {regex.match(line) for line in open(filename)} if match} def find_options(self, filename): return self.find_pattern(filename, self.option_pattern) def find_functions(self, filename): return self.find_pattern(filename, self.function_pattern) def find_alternates(self): command = shlex.split('grep --no-filename OPT_ALT') for source_file in ['session', 'report']: command.append(os.path.join(self.source, 'src', '%s.cc' % source_file)) try: output = subprocess.check_output(command).split('\n'); except subprocess.CalledProcessError: output = '' regex = re.compile(r'OPT_ALT\([^,]*,\s*([^)]+?)_?\)'); alternates = {match.group(1).replace('_', '-') for match in {regex.search(line) for line in output} if match} return alternates def ledger_options(self): pipe = Popen('%s --debug option.names parse true' % self.ledger, shell=True, stdout=PIPE, stderr=PIPE) regex = re.compile('\[DEBUG\]\s+Option:\s+(.*?)_?$') ledger_options = {match.group(1).replace('_', '-') for match in {regex.search(line.decode()) for line in pipe.stderr.readlines()} if match} return ledger_options def ledger_functions(self): command = shlex.split('grep --no-filename fn_ %s' % (os.path.join(self.source, 'src', 'report.h'))) try: output = subprocess.check_output(command).split('\n'); except subprocess.CalledProcessError: output = '' regex = re.compile(r'fn_([^(]+)\('); functions = {match.group(1) for match in {regex.search(line) for line in output} if match} return functions def main(self): options = self.find_options(self.source_file) for option in self.ledger_options(): if option not in options: self.missing_options.add(option) else: options.remove(option) known_alternates = self.find_alternates() self.unknown_options = {option for option in options if option not in known_alternates} functions = self.find_functions(self.source_file) for function in self.ledger_functions(): if function not in functions: self.missing_functions.add(function) else: functions.remove(function) known_functions = ['tag', 'has_tag', 'meta', 'has_meta'] self.unknown_functions = {function for function in functions if function not in known_functions} if len(self.missing_options): print("Missing %s option entries for:%s%s\n" % (self.source_type, self.sep, self.sep.join(sorted(list(self.missing_options))))) if len(self.unknown_options): print("%s entry for unknown options:%s%s\n" % (self.source_type, self.sep, self.sep.join(sorted(list(self.unknown_options))))) if len(self.missing_functions): print("Missing %s function entries for:%s%s\n" % (self.source_type, '\n ', '\n '.join(sorted(list(self.missing_functions))))) if len(self.unknown_functions): print("%s entry for unknown functions:%s%s\n" % (self.source_type, '\n ', '\n '.join(sorted(list(self.unknown_functions))))) errors = len(self.missing_options) + len(self.unknown_options) + len(self.missing_functions) + len(self.unknown_functions) return errors ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/CheckTexinfo.py�������������������������������������������������������������������0000775�0000000�0000000�00000007033�14411236400�0016665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import re import os import argparse from os.path import * from subprocess import Popen, PIPE from CheckOptions import CheckOptions class CheckTexinfo (CheckOptions): def __init__(self, args): CheckOptions.__init__(self, args) self.option_pattern = '^@item\s+--([-A-Za-z]+)' self.function_pattern = '^@defun\s+([-A-Za-z_]+)' self.source_file = join(self.source, 'doc', 'ledger3.texi') self.source_type = 'texinfo' def find_functions(self, filename): functions = set() state_normal = 0 state_function = 1 state = state_normal function = None fun_doc = str() fun_example = False item_regex = re.compile(self.function_pattern) itemx_regex = re.compile('^@defunx') example_regex = re.compile('^@smallexample\s+@c\s+command:') fix_regex = re.compile('FIX') comment_regex = re.compile('^\s*@c') for line in open(filename): line = line.strip() if state == state_normal: match = item_regex.match(line) if match: state = state_function function = match.group(1) elif state == state_function: if line == '@end defun': if function and fun_example and len(fun_doc) and not fix_regex.search(fun_doc): functions.add(function) state = state_normal fun_example = None fun_doc = str() elif itemx_regex.match(line): continue elif example_regex.match(line): fun_example = True elif not comment_regex.match(line): fun_doc += line return functions def find_options(self, filename): options = set() state_normal = 0 state_option_table = 1 state = state_normal option = None opt_doc = str() item_regex = re.compile(self.option_pattern) itemx_regex = re.compile('^@itemx') fix_regex = re.compile('FIX') comment_regex = re.compile('^\s*@c') for line in open(filename): line = line.strip() if state == state_normal: if line == '@ftable @option': state = state_option_table elif state == state_option_table: if line == '@end ftable': if option and len(opt_doc) and not fix_regex.search(opt_doc): options.add(option) state = state_normal option = None continue match = item_regex.match(line) if match: if option and len(opt_doc) and not fix_regex.search(opt_doc): options.add(option) option = match.group(1) opt_doc = str() elif itemx_regex.match(line): continue elif not comment_regex.match(line): opt_doc += line return options if __name__ == "__main__": def getargs(): parser = argparse.ArgumentParser(prog='CheckTexinfo', description='Check that ledger options are documented in the texinfo manual') parser.add_argument('-l', '--ledger', dest='ledger', type=str, action='store', required=True, help='the path to the ledger executable to test with') parser.add_argument('-s', '--source', dest='source', type=str, action='store', required=True, help='the path to the top level ledger source directory') return parser.parse_args() args = getargs() script = CheckTexinfo(args) status = script.main() sys.exit(status) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/ConfirmTests.py�������������������������������������������������������������������0000775�0000000�0000000�00000005424�14411236400�0016735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # This script confirms both that the register report "adds up", and that its # final balance is the same as what the balance report shows. import sys import os import re from LedgerHarness import LedgerHarness harness = LedgerHarness(sys.argv) tests = sys.argv[3] if not os.path.isdir(tests) and not os.path.isfile(tests): sys.stderr.write("'%s' is not a directory or file (cwd %s)" % (tests, os.getcwd())) sys.exit(1) commands = [ "-f '$tests/standard.dat' -O 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728", "-f '$tests/standard.dat' -B c56a21d23a6535184e7152ee138c28974f14280c", "-f '$tests/standard.dat' -V c56a21d23a6535184e7152ee138c28974f14280c", "-f '$tests/standard.dat' -G c56a21d23a6535184e7152ee138c28974f14280c", "-f '$tests/standard.dat' -B c0226fafdf9e6711ac9121cf263e2d50791859cb", "-f '$tests/standard.dat' -V c0226fafdf9e6711ac9121cf263e2d50791859cb", "-f '$tests/standard.dat' -G c0226fafdf9e6711ac9121cf263e2d50791859cb" ] def clean(num): num = re.sub("(\s+|\$|,)","", num) m = re.search("([-0-9.]+)", num) if m: return float(m.group(1)) else: return float(num) def confirm_report(command): index = 1 last_line = "" failure = False running_total = 0.0 p = harness.run(re.sub('\$cmd', 'reg', command)) for line in harness.readlines(p.stdout): match = re.match("\\s*([-$,0-9.]+)\\s+([-$,0-9.]+)", line[54:]) if not match: continue value = clean(match.group(1)) total = clean(match.group(2)) running_total += value diff = abs(running_total - total) if re.search(' -[VGB] ', command) and diff < 0.015: diff = 0.0 if diff > 0.001: print("DISCREPANCY: %.3f (%.3f - %.3f) at line %d:" % \) (running_total - total, running_total, total, index) print(line,) running_total = total failure = True index += 1 last_line = line balance_total = 0.0 p = harness.run(re.sub('\$cmd', 'bal', command)) for line in harness.readlines(p.stdout): if line[0] != '-': balance_total = clean(line[:20]) diff = abs(balance_total - running_total) if re.search(' -[VGB] ', command) and diff < 0.015: diff = 0.0 if diff > 0.001: print() print("DISCREPANCY: %.3f (%.3f - %.3f) between register and balance" % \) (balance_total - running_total, balance_total, running_total) print(last_line,) failure = True return not failure for cmd in commands: if confirm_report('$ledger --rounding $cmd ' + re.sub('\$tests', tests, cmd)): harness.success() else: harness.failure() harness.exit() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/DocTests.py�����������������������������������������������������������������������0000775�0000000�0000000�00000023216�14411236400�0016044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- from io import open import os import re import sys import shlex import locale import hashlib import argparse import subprocess from difflib import unified_diff class DocTests: def __init__(self, args): scriptpath = os.path.dirname(os.path.realpath(__file__)) self.ledger = os.path.realpath(args.ledger) self.sourcepath = os.path.realpath(args.file) self.verbose = args.verbose self.tests = args.examples self.examples = dict() self.test_files = list() self.testin_token = 'command' self.testout_token = 'output' self.testdat_token = 'input' self.testfile_token = 'file' self.validate_token = 'validate' self.validate_cmd_token = 'validate-command' self.validate_dat_token = 'validate-data' self.testwithdat_token = 'with_input' self.testwithfile_token = 'with_file' def read_example(self): endexample = re.compile(r'^@end\s+smallexample\s*$') example = str() while True: line = self.file.readline() self.current_line += 1 if len(line) <= 0 or endexample.match(line): break # Replace special texinfo character sequences with their ASCII counterpart example += re.sub(r'@([@{}])', r'\1', line) return example def test_id(self, example): example_id = example.rstrip().encode('utf-8') return hashlib.sha1(example_id).hexdigest()[0:7].upper() def find_examples(self): startexample = re.compile(r'^@smallexample\s+@c\s+(%s|%s|%s|%s)(?::([\dA-Fa-f]+|validate))?(?:,(.*))?' % (self.testin_token, self.testout_token, self.testdat_token, self.testfile_token)) while True: line = self.file.readline() self.current_line += 1 if len(line) <= 0: break startmatch = startexample.match(line) if (startmatch): test_begin_pos = self.file.tell() test_begin_line = self.current_line test_kind = startmatch.group(1) test_id = startmatch.group(2) test_options = dict() for pair in re.split(r',\s*', str(startmatch.group(3))): kv = re.split(r':\s*', pair, 2) try: test_options[kv[0]] = kv[1] except IndexError: pass example = self.read_example() test_end_pos = self.file.tell() test_end_line = self.current_line if not test_id: print('Example', test_kind, 'in line', test_begin_line, 'is missing id.', file=sys.stderr) test_id = self.test_id(example) if test_kind == self.testin_token: print('Use', self.test_id(example), file=sys.stderr) elif test_kind == self.testin_token and test_id != self.validate_token and test_id != self.test_id(example): print('Expected test id', test_id, 'for example' \ , test_kind, 'on line', test_begin_line, 'to be', self.test_id(example), file=sys.stderr) if test_id == self.validate_token: test_id = "Val-" + str(test_begin_line) if test_kind == self.testin_token: test_kind = self.validate_cmd_token elif test_kind == self.testdat_token: test_kind = self.validate_dat_token try: self.examples[test_id] except KeyError: self.examples[test_id] = dict() try: example = self.examples[test_id][test_kind][test_kind] + example except KeyError: pass self.examples[test_id][test_kind] = { 'bpos': test_begin_pos, 'epos': test_end_pos, 'blin': test_begin_line, 'elin': test_end_line, 'opts': test_options, test_kind: example, } def parse_command(self, test_id, example): validate_command = False try: command = example[self.testin_token][self.testin_token] command = re.sub(r'\\\n', '', command) except KeyError: if self.validate_dat_token in example: command = '$ ledger bal' elif self.validate_cmd_token in example: validate_command = True command = example[self.validate_cmd_token][self.validate_cmd_token] else: return None if sys.version_info.major == 2: command = command.encode(locale.getpreferredencoding()) command_parts = shlex.split(command) command = filter(lambda x: x != '\n', command_parts) if sys.version_info.major > 2: command = list(command) if command[0] == '$': command.remove('$') index = command.index('ledger') command[index] = self.ledger for i,argument in enumerate(shlex.split('--args-only --columns 80')): command.insert(index+i+1, argument) try: findex = command.index('-f') except ValueError: try: findex = command.index('--file') except ValueError: findex = index+1 command.insert(findex, '--file') if validate_command: command.insert(findex+1, 'sample.dat') else: command.insert(findex+1, test_id + '.dat') return (command, findex+1) def test_examples(self): failed = set() tests = self.examples.keys() if self.tests: tests = list(set(self.tests).intersection(tests)) temp = list(set(self.tests).difference(tests)) if len(temp) > 0: print('Skipping non-existent examples: %s' % ', '.join(temp), file=sys.stderr) for test_id in tests: validation = False if self.validate_dat_token in self.examples[test_id] or self.validate_cmd_token in self.examples[test_id]: validation = True example = self.examples[test_id] try: (command, findex) = self.parse_command(test_id, example) except TypeError: failed.add(test_id) continue output = example.get(self.testout_token, {}).get(self.testout_token) input = example.get(self.testdat_token, {}).get(self.testdat_token) if not input: with_input = example.get(self.testin_token, {}).get('opts', {}).get(self.testwithdat_token) input = self.examples.get(with_input, {}).get(self.testdat_token, {}).get(self.testdat_token) if not input: input = example.get(self.validate_dat_token, {}).get(self.validate_dat_token) if command and (output != None or validation): test_file_created = False if findex: scriptpath = os.path.dirname(os.path.realpath(__file__)) test_input_dir = os.path.join(scriptpath, '..', 'test', 'input') test_file = command[findex] if not os.path.exists(test_file): if input: test_file_created = True with open(test_file, 'w', encoding='utf-8') as f: f.write(input) elif os.path.exists(os.path.join(test_input_dir, test_file)): command[findex] = os.path.join(test_input_dir, test_file) try: convert_idx = command.index(str('convert')) convert_file = command[convert_idx+1] convert_data = example[self.testfile_token][self.testfile_token] if not os.path.exists(convert_file): with open(convert_file, 'w', encoding='utf-8') as f: f.write(convert_data) except ValueError: pass error = None try: verify = subprocess.check_output(command, stderr=subprocess.STDOUT) verify = verify.decode('utf-8') if sys.platform == 'win32': verify = verify.replace('\r\n', '\n') valid = (output == verify) or (not error and validation) except subprocess.CalledProcessError as e: error = e.output valid = False failed.add(test_id) if valid and test_file_created: os.remove(test_file) if self.verbose > 0: print(test_id, ':', 'Passed' if valid else 'FAILED: {}'.format(error) if error else 'FAILED') else: sys.stdout.write('.' if valid else 'E') sys.stdout.flush() if not (valid or error): failed.add(test_id) if self.verbose > 1: print(' '.join(command)) if not validation: for line in unified_diff(output.split('\n'), verify.split('\n'), fromfile='generated', tofile='expected'): print(line) print() else: if self.verbose > 0: print(test_id, ':', 'Skipped') else: sys.stdout.write('X') if self.verbose == 0: print() if len(failed) > 0: print("\nThe following examples failed:") print(" ", "\n ".join(failed)) return len(failed) def main(self): self.file = open(self.sourcepath, encoding='utf-8') self.current_line = 0 self.find_examples() failed_examples = self.test_examples() self.file.close() return failed_examples if __name__ == "__main__": def getargs(): parser = argparse.ArgumentParser(prog='DocTests', description='Test and validate ledger examples from the texinfo manual') parser.add_argument('-v', '--verbose', dest='verbose', action='count', default=0, help='be verbose. Add -vv for more verbosity') parser.add_argument('-l', '--ledger', dest='ledger', type=str, action='store', required=True, help='the path to the ledger executable to test with') parser.add_argument('-f', '--file', dest='file', type=str, action='store', required=True, help='the texinfo documentation file to run the examples from') parser.add_argument('examples', metavar='EXAMPLE', type=str, nargs='*', help='the examples to test') return parser.parse_args() args = getargs() script = DocTests(args) status = script.main() sys.exit(status) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/GenerateTests.py������������������������������������������������������������������0000775�0000000�0000000�00000010070�14411236400�0017063�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # This script confirms both that the register report "adds up", and that its # final balance is the same as what the balance report shows. import sys import re from difflib import ndiff multiproc = False try: from multiprocessing import Pool multiproc = True except: pass args = sys.argv jobs = 1 match = re.match('-j([0-9]+)?', args[1]) if match: args = [args[0]] + args[2:] if match.group(1): jobs = int(match.group(1)) if jobs == 1: multiproc = False from LedgerHarness import LedgerHarness harness = LedgerHarness(args) #def normalize(line): # match = re.match("((\s*)([A-Za-z]+)?(\s*)([-0-9.]+)(\s*)([A-Za-z]+)?)( (.+))?$", line) # if match: # if match.group(3): # prefix = match.group(3) + " " + match.group(5) # if match.group(8): # return prefix + match.group(8) # return prefix # elif match.group(7): # prefix = match.group(7) + " " + match.group(5) # if match.group(8): # return prefix + match.group(8) # return prefix # return line def generation_test(seed): p_gen = harness.run('$ledger --seed=%d generate' % seed) cout = harness.read(p_gen.stdout) if not harness.wait(p_gen, msg=("Generation for seed %d failed:" % seed)): return False p_print = harness.run('$ledger --actual -f - print') p_print.stdin.write(cout) p_print.stdin.close() p_print_out = p_print.stdout.read() if not harness.wait(p_print, msg=("Print for seed %d failed:" % seed)): return False #p_cerr_bal = Popen("%s --args-only -f - bal" % ledger, shell=True, # stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) #p_cerr_bal.stdin.write(cerr) #p_cerr_bal.stdin.close() # #cerr_lines = [normalize(line) for line in p_cerr_bal.stdout.readlines()] # #if p_cerr_bal.wait() != 0: # print("Stderr balance for seed %d failed due to error:" % seed) # print(p_cerr_bal.stderr.read()) # del p_cerr_bal # return False #del p_cerr_bal p_cout_bal = harness.run('$ledger -f - bal') p_cout_bal.stdin.write(cout) p_cout_bal.stdin.close() cout_lines = harness.readlines(p_cout_bal.stdout) if len(cout_lines) == 0: return False #norm_cout_lines = [normalize(line) for line in cout_lines] if not harness.wait(p_cout_bal, msg=("Stdout balance for seed %d failed:" % seed)): return False p_print_bal = harness.run('$ledger -f - bal') p_print_bal.stdin.write(p_print_out) p_print_bal.stdin.close() print_lines = harness.readlines(p_print_bal.stdout) if len(print_lines) == 0: return False if not harness.wait(p_print_bal, msg=("Print balance for seed %d failed:" % seed)): return False success = True #printed = False #for line in ndiff(cerr_lines, norm_cout_lines, charjunk=None): # if line[:2] == " ": # continue # if not printed: # if success: print() # print("Generation failure in output from seed %d (cerr vs. cout):" % seed) # if success: failed += 1 # success = False # printed = True # print(" ", line) printed = False for line in ndiff(cout_lines, print_lines, charjunk=None): if line[:2] == " ": continue if not printed: if success: print() print("Generation failure in output from seed %d (cout vs. print):" % seed) success = False printed = True print(" ", line) return success beg_range = 1 end_range = 20 if len(args) > 4: beg_range = int(args[3]) end_range = int(args[4]) def run_gen_test(i): if generation_test(i): harness.success() else: harness.failure() return harness.failed if multiproc: pool = Pool(jobs*2) else: pool = None if pool: pool.map(run_gen_test, range(beg_range, end_range)) else: for i in range(beg_range, end_range): run_gen_test(i) if pool: pool.close() pool.join() harness.exit() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/LedgerHarness.py������������������������������������������������������������������0000775�0000000�0000000�00000011373�14411236400�0017043�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import os import re from subprocess import Popen, PIPE import types if sys.version_info.major == 2: import copy_reg as copyreg else: import copyreg def _pickle_method(method): func_name = method.im_func.__name__ obj = method.im_self cls = method.im_class return _unpickle_method, (func_name, obj, cls) def _unpickle_method(func_name, obj, cls): for cls in cls.mro(): try: func = cls.__dict__[func_name] except KeyError: pass else: break return func.__get__(obj, cls) copyreg.pickle(types.MethodType, _pickle_method, _unpickle_method) class LedgerHarness: ledger = None sourcepath = None succeeded = 0 failed = 0 verify = False gmalloc = False python = False def __init__(self, argv): if not os.path.isfile(argv[1]): print("Cannot find ledger at '%s'" % argv[1]) sys.exit(1) if not os.path.isdir(argv[2]): print("Cannot find source path at '%s'" % argv[2]) sys.exit(1) self.ledger = os.path.realpath(argv[1]) self.sourcepath = os.path.realpath(argv[2]) self.succeeded = 0 self.failed = 0 self.verify = '--verify' in argv self.gmalloc = '--gmalloc' in argv self.python = '--python' in argv def run(self, command, verify=None, gmalloc=None, columns=True): env = os.environ.copy() if (gmalloc is not None and gmalloc) or \ (gmalloc is None and self.gmalloc): env['MallocGuardEdges'] = '1' env['MallocScribble'] = '1' env['MallocPreScribble'] = '1' env['MallocCheckHeapStart'] = '1000' env['MallocCheckHeapEach'] = '10000' env['DYLD_INSERT_LIBRARIES'] = '/usr/lib/libgmalloc.dylib' env['MALLOC_PROTECT_BEFORE'] = '1' env['MALLOC_FILL_SPACE'] = '1' env['MALLOC_STRICT_SIZE'] = '1' if (verify is not None and verify) or \ (verify is None and self.verify): insert = ' --verify' else: insert = '' if columns: insert += ' --columns=80' command = command.replace('$ledger', '"%s"%s %s' % \ (self.ledger, insert, '--args-only')) valgrind = '/usr/bin/valgrind' if not os.path.isfile(valgrind): valgrind = '/opt/local/bin/valgrind' if os.path.isfile(valgrind) and '--verify' in insert: command = valgrind + ' -q ' + command # If we are running under msys2, use bash to execute the test commands if 'MSYSTEM' in os.environ: bash_path = os.environ['MINGW_PREFIX'] + '/../usr/bin/bash.exe' return Popen([bash_path, '-c', command], shell=False, close_fds=False, env=env, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self.sourcepath) return Popen(command, shell=True, close_fds=True, env=env, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self.sourcepath) def read(self, fd): text = "" text_data = os.read(fd.fileno(), 8192) while text_data: if text_data: text += text_data text_data = os.read(fd.fileno(), 8192) return text def readlines(self, fd): lines = [] for line in fd.readlines(): if sys.version_info.major == 2: line = unicode(line, 'utf-8') else: line = line.decode('utf-8') if not line.startswith('GuardMalloc'): lines.append(line) return lines def wait(self, process, msg='Ledger invocation failed:'): if process.wait() != 0: print(msg) print(process.stderr.read()) self.failure() return False return True def success(self): sys.stdout.write(".") sys.stdout.flush() self.succeeded += 1 def failure(self, name=None): sys.stdout.write("E") if name: sys.stdout.write("[%s]" % name) sys.stdout.flush() self.failed += 1 def exit(self): print() if self.succeeded > 0: print("OK (%d) " % self.succeeded,) if self.failed > 0: print("FAILED (%d)" % self.failed,) print() sys.exit(self.failed) if __name__ == '__main__': harness = LedgerHarness(sys.argv) proc = harness.run('$ledger -f doc/sample.dat reg') print('STDOUT:') print(proc.stdout.read()) print('STDERR:') print(proc.stderr.read()) harness.success() harness.exit() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/PyUnitTests.py��������������������������������������������������������������������0000775�0000000�0000000�00000000667�14411236400�0016574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash set -e PYTHONPATH="%builddir%/.libs":$PYTHONPATH \ LD_LIBRARY_PATH="%builddir%/.libs":$LD_LIBRARY_PATH \ DYLD_LIBRARY_PATH="%builddir%/.libs":$DYLD_LIBRARY_PATH \ %python% "%builddir%"/test/python/ConvertedTests.py PYTHONPATH="%builddir%/.libs":$PYTHONPATH \ LD_LIBRARY_PATH="%builddir%/.libs":$LD_LIBRARY_PATH \ DYLD_LIBRARY_PATH="%builddir%/.libs":$DYLD_LIBRARY_PATH \ %python% "%srcdir%"/test/python/UnitTests.py �������������������������������������������������������������������������ledger-3.3.2/test/RegressTests.py�������������������������������������������������������������������0000775�0000000�0000000�00000017432�14411236400�0016754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- from io import open import sys import os import re import tempfile multiproc = False try: from multiprocessing import Pool multiproc = True except: pass from difflib import unified_diff from LedgerHarness import LedgerHarness args = sys.argv jobs = 1 match = re.match('-j([0-9]+)?', args[1]) if match: args = [args[0]] + args[2:] if match.group(1): jobs = int(match.group(1)) if jobs == 1: multiproc = False harness = LedgerHarness(args) tests = args[3] if not os.path.isdir(tests) and not os.path.isfile(tests): sys.stderr.write("'%s' is not a directory or file (cwd %s)" % (tests, os.getcwd())) sys.exit(1) class RegressFile(object): def __init__(self, filename): self.filename = filename self.fd = open(self.filename, encoding='utf-8') def transform_line(self, line): line = line.replace('$sourcepath', harness.sourcepath) line = line.replace('$FILE', os.path.realpath(self.filename)) return line def read_test(self): test = { 'command': None, 'output': None, 'error': None, 'exitcode': 0 } in_output = False in_error = False line = self.fd.readline() if sys.version_info.major == 2 and type(line) is str: line = unicode(line, 'utf-8') while line: if line.startswith("test "): command = line[5:] match = re.match('(.*) -> ([0-9]+)', command) if match: test['command'] = self.transform_line(match.group(1)) test['exitcode'] = int(match.group(2)) else: test['command'] = command in_output = True elif in_output: if line.startswith("end test"): in_output = in_error = False break elif in_error: if test['error'] is None: test['error'] = [] test['error'].append(self.transform_line(line)) else: if line.startswith("__ERROR__"): in_error = True else: if test['output'] is None: test['output'] = [] test['output'].append(self.transform_line(line)) line = self.fd.readline() if sys.version_info.major == 2 and type(line) is str: line = unicode(line, 'utf-8') #print("line =", line) return test['command'] and test def notify_user(self, msg, test): print(msg) print("--") print(self.transform_line(test['command']),) print("--") def run_test(self, test): use_stdin = False if sys.platform == 'win32': test['command'] = test['command'].replace('/dev/null', 'nul') # There is no equivalent to /dev/stdout, /dev/stderr, /dev/stdin # on Windows, so skip tests that require them. if '/dev/std' in test['command']: harness.success() return if test['command'].find("-f ") != -1: test['command'] = '$ledger ' + test['command'] if re.search("-f (-|/dev/stdin)(\s|$)", test['command']): use_stdin = True else: test['command'] = (('$ledger -f "%s" ' % os.path.realpath(self.filename)) + test['command']) p = harness.run(test['command'], columns=(not re.search('--columns', test['command']))) if use_stdin: fd = open(self.filename, encoding='utf-8') try: stdin = fd.read() if sys.version_info.major > 2: stdin = stdin.encode('utf-8') p.stdin.write(stdin) finally: fd.close() p.stdin.close() success = True printed = False index = 0 if test['output'] is not None: process_output = harness.readlines(p.stdout) expected_output = test['output'] if sys.platform == 'win32': process_output = [l.replace('\r\n', '\n').replace('\\', '/') for l in process_output] # Replace \ with / in the expected output because the line above # makes it impossible for the process output to have a \. expected_output = [l.replace('\\', '/') for l in expected_output] for line in unified_diff(expected_output, process_output): index += 1 if index < 3: continue if not printed: if success: print self.notify_user("FAILURE in output from %s:" % self.filename, test) success = False printed = True if sys.version_info.major == 2 and type(line) is str: line = unicode(line, 'utf-8') print(' ', line,) printed = False index = 0 process_error = harness.readlines(p.stderr) if test['error'] is not None or process_error is not None: if test['error'] is None: test['error'] = [] if sys.platform == 'win32': process_error = [l.replace('\r\n', '\n').replace('\\', '/') for l in process_error] test['error'] = [l.replace('\\', '/') for l in test['error']] for line in unified_diff(test['error'], process_error): index += 1 if index < 3: continue if not printed: if success: print self.notify_user("FAILURE in error output from %s:" % self.filename, test) success = False printed = True print(" ", line,) if test['exitcode'] == p.wait(): if success: harness.success() else: harness.failure(os.path.basename(self.filename)) print("STDERR:") print(p.stderr.read()) else: if success: print self.notify_user("FAILURE in exit code (%d != %d) from %s:" % (test['exitcode'], p.returncode, self.filename), test) harness.failure(os.path.basename(self.filename)) def run_tests(self): if os.path.getsize(self.filename) == 0: print("WARNING: Empty testfile detected: %s" % (self.filename), file=sys.stderr) harness.failure(os.path.basename(self.filename)) return False test = self.read_test() while test: self.run_test(test) test = self.read_test() def close(self): self.fd.close() def do_test(path): entry = RegressFile(path) entry.run_tests() entry.close() if __name__ == '__main__': if multiproc: pool = Pool(jobs*2) else: pool = None if os.path.isdir(tests): tests = [os.path.join(tests, x) for x in os.listdir(tests) if (x.endswith('.test') and (not '_py.test' in x or (harness.python and not harness.verify)))] if pool: pool.map(do_test, tests, 1) else: map(do_test, tests) else: entry = RegressFile(tests) entry.run_tests() entry.close() if pool: pool.close() pool.join() harness.exit() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0015515�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/CMakeLists.txt�����������������������������������������������������������0000664�0000000�0000000�00000000043�14411236400�0020252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������add_ledger_harness_tests(Baseline) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-accounts.test��������������������������������������������������������0000664�0000000�0000000�00000001312�14411236400�0020773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011-01-01 * Opening balance Assets:Bank 10.00 GBP Equity:Opening balance 2012-01-02 * List XXX before AAA to test sorting Assets:XXX 5.00 GBP Assets:Bank 2012-01-03 * List AAA after XXX to test sorting Assets:AAA 3.00 GBP Assets:Bank 2012-01-03 * Account name with UTF-8 Assets:♚ 3.00 GBP Assets:Testing123ÕßDone test accounts Assets:AAA Assets:Bank Assets:Testing123ÕßDone Assets:XXX Assets:♚ Equity:Opening balance end test test accounts assets:a Assets:AAA end test test accounts b Assets:Bank Equity:Opening balance end test test accounts ß Assets:Testing123ÕßDone end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-balance.test���������������������������������������������������������0000664�0000000�0000000�00000003571�14411236400�0020552�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2012-01-01 * Opening balances Assets:A 10.00 Equity:Opening balances -10.00 2012-01-02 * A to B Assets:A -10.00 Assets:B 10.00 2012-01-03 * B partly to C Assets:B -5.00 Assets:C 5.00 2012-01-04 * Borrow Assets:A 10.00 Liabilities:A -10.00 2012-01-05 * Return A Assets:A -10.00 Liabilities:A 10.00 test bal 10 Assets 5 B 5 C -10 Equity:Opening balances -------------------- 0 end test test bal -n 10 Assets -10 Equity -------------------- 0 end test test bal -n -E 10 Assets -10 Equity 0 Liabilities -------------------- 0 end test test bal -E 10 Assets 0 A 5 B 5 C -10 Equity:Opening balances 0 Liabilities:A -------------------- 0 end test test bal --flat 5 Assets:B 5 Assets:C -10 Equity:Opening balances -------------------- 0 end test test bal --flat -E 0 Assets:A 5 Assets:B 5 Assets:C -10 Equity:Opening balances 0 Liabilities:A -------------------- 0 end test test bal -E --flat --no-total 0 Assets:A 5 Assets:B 5 Assets:C -10 Equity:Opening balances 0 Liabilities:A end test test bal -n --flat end test ���������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-budget.test����������������������������������������������������������0000664�0000000�0000000�00000004211�14411236400�0020427�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly Expenses:Phone 10.00 GBP Expenses:Rent 550.00 GBP Assets 2012-01-10 * Phone expense on holidays Expenses:Phone 12.00 EUR @@ 10.00 GBP Assets:Cash -10.00 GBP 2012-01-31 * Rent expense Expenses:Rent 550.00 GBP Assets:Cash -550.00 GBP 2012-02-28 * Phone expense Expenses:Phone 20.00 GBP Assets:Cash -20.00 GBP 2012-02-29 * Rent expense Expenses:Rent 530.00 GBP Assets:Cash -530.00 GBP 2012-03-10 * Phone expense Expenses:Phone 15.00 GBP Assets:Cash -15.00 GBP 2012-03-31 * Rent expense Expenses:Rent 570.00 GBP Assets:Cash -570.00 GBP test budget -X GBP -p "in january 2012" -560.00 GBP -560.00 GBP 0 100% Assets 560.00 GBP 560.00 GBP 0 100% Expenses 10.00 GBP 10.00 GBP 0 100% Phone 550.00 GBP 550.00 GBP 0 100% Rent ------------ ------------ ------------ ----- 0 0 0 0 end test test budget -X GBP -p "in feb 2012" -550.00 GBP -560.00 GBP 10.00 GBP 98% Assets 550.00 GBP 560.00 GBP -10.00 GBP 98% Expenses 20.00 GBP 10.00 GBP 10.00 GBP 200% Phone 530.00 GBP 550.00 GBP -20.00 GBP 96% Rent ------------ ------------ ------------ ----- 0 0 0 0 end test test budget -X GBP -p "in march 2012" -585.00 GBP -560.00 GBP -25.00 GBP 104% Assets 585.00 GBP 560.00 GBP 25.00 GBP 104% Expenses 15.00 GBP 10.00 GBP 5.00 GBP 150% Phone 570.00 GBP 550.00 GBP 20.00 GBP 104% Rent ------------ ------------ ------------ ----- 0 0 0 0 end test test budget -X GBP --now "2012-03-31" -1695.00 GBP -1680.00 GBP -15.00 GBP 101% Assets 1695.00 GBP 1680.00 GBP 15.00 GBP 101% Expenses 45.00 GBP 30.00 GBP 15.00 GBP 150% Phone 1650.00 GBP 1650.00 GBP 0 100% Rent ------------ ------------ ------------ ----- 0 0 0 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-cleared.test���������������������������������������������������������0000664�0000000�0000000�00000001545�14411236400�0020563�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-02-23 * Test 1 A 10.00 B 2012-02-24 Test 1 C 15.00 D ; leave E/F uncleared 2012-02-25 Test 1 E 20.00 F ; have a cleared posting last for C 2012-02-26 * Test 1 C 30.00 G ; have an uncleared posting last for A 2012-02-27 Test 1 A 40.00 H test cleared 50 10 12-Feb-23 A -10 -10 12-Feb-23 B 45 30 12-Feb-26 C -15 0 D 20 0 E -20 0 F -30 -30 12-Feb-26 G -40 0 H ---------------- ---------------- --------- 0 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-commodities.test�����������������������������������������������������0000664�0000000�0000000�00000001026�14411236400�0021472�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011-01-01 * Opening balance Assets:Bank 10.00 GBP Equity:Opening balance 2011-03-04 * Buy AAA Assets:Broker 2 AAA @ 0.90 GBP Assets:Bank -1.80 GBP 2011-03-05 * Buy AA2 Assets:Broker 2 "AA2" @ 1.00 GBP Assets:Bank 2011-03-06 * Get Miles&More airmiles Assets:Rewards 1000 "M&M" Income:Rewards test commodities "AA2" "M&M" AAA GBP end test test commodities Assets:Rewards "M&M" end test test commodities no:such:account end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-convert.test���������������������������������������������������������0000664�0000000�0000000�00000003144�14411236400�0020641�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert1.dat 2011/12/12=2011/12/13 * (100) Test ;test Expenses:Unknown $10 Equity:Unknown $-10 = $20 2011/12/12=2011/12/12 * Expenses:Unknown $10 Equity:Unknown end test test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert2.dat 2011/01/01 * test Expenses:Unknown 20.00 EUR Equity:Unknown end test test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert3.dat -> 1 __ERROR__ While parsing file "$sourcepath/test/baseline/cmd-convert3.dat", line 1: While parsing CSV line: 01/01/2011,, Error: Only one posting with null amount allowed per transaction end test test -f /dev/null convert test/baseline/cmd-convert4.dat -> 1 __ERROR__ While parsing file "$sourcepath/test/baseline/cmd-convert4.dat", line 1: While parsing CSV line: bogus,$10, Error: Invalid date: bogus end test test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert5.dat 2011/01/01 * test1 Expenses:Unknown 20.00 EUR Equity:Unknown 2011/01/02 * test2 Expenses:Unknown -10.00 EUR Equity:Unknown end test test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert6.dat -> 1 __ERROR__ While parsing file "$sourcepath/test/baseline/cmd-convert6.dat", line 1: While parsing CSV line: 01/01/2011,20.00 EUR,10.00 EUR,test1, Error: Cannot have two values for a single transaction end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-convert1.dat���������������������������������������������������������0000664�0000000�0000000�00000000165�14411236400�0020513�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,posted,code,payee,amount,total,note, 12/12/2011,12/13/2011,100,Test,$10,$20,test, 12/12/2011,12/12/2011,,,$10,, �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-convert2.dat���������������������������������������������������������0000664�0000000�0000000�00000000055�14411236400�0020512�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,amount,desc, 01/01/2011,20.00 EUR,test, �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-convert3.dat���������������������������������������������������������0000664�0000000�0000000�00000000032�14411236400�0020506�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,amount, 01/01/2011,, ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-convert4.dat���������������������������������������������������������0000664�0000000�0000000�00000000030�14411236400�0020505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,amount, bogus,$10, ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-convert5.dat���������������������������������������������������������0000664�0000000�0000000�00000000121�14411236400�0020507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,credit,debit,desc, 01/01/2011,20.00 EUR,,test1, 01/02/2011,,10.00 EUR,test2,�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-convert6.dat���������������������������������������������������������0000664�0000000�0000000�00000000075�14411236400�0020520�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,credit,debit,desc, 01/01/2011,20.00 EUR,10.00 EUR,test1,�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-csv.test�������������������������������������������������������������0000664�0000000�0000000�00000003726�14411236400�0017762�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2012-01-01 * Opening balances Assets:A 10.00 Equity:Opening balances -10.00 2012-01-02 * Cleared posting Assets:A -10.00 Assets:B 10.00 2012-01-03 Uncleared posting Assets:B -5.00 Assets:C 5.00 2012-01-04=2012-01-05 * aux date Assets:A 10.00 Liabilities:A -10.00 2012-01-05 * (100) Code Assets:A -10.00 Liabilities:A 10.00 2012-01-06 * (100) Specify commodity Assets:A $-10.00 Liabilities:A $10.00 2012-01-07 * (100) Specify commodity Assets:A -10.00 EUR Liabilities:A 10.00 EUR 2012-01-08 * (100) With note ;This is an xact note Assets:A -10.00 EUR Liabilities:A 10.00 EUR test csv "2012/01/01","","Opening balances","Assets:A","","10","*","" "2012/01/01","","Opening balances","Equity:Opening balances","","-10","*","" "2012/01/02","","Cleared posting","Assets:A","","-10","*","" "2012/01/02","","Cleared posting","Assets:B","","10","*","" "2012/01/03","","Uncleared posting","Assets:B","","-5","","" "2012/01/03","","Uncleared posting","Assets:C","","5","","" "2012/01/04","","aux date","Assets:A","","10","*","" "2012/01/04","","aux date","Liabilities:A","","-10","*","" "2012/01/05","100","Code","Assets:A","","-10","*","" "2012/01/05","100","Code","Liabilities:A","","10","*","" "2012/01/06","100","Specify commodity","Assets:A","$","-10","*","" "2012/01/06","100","Specify commodity","Liabilities:A","$","10","*","" "2012/01/07","100","Specify commodity","Assets:A","EUR","-10","*","" "2012/01/07","100","Specify commodity","Liabilities:A","EUR","10","*","" "2012/01/08","100","With note","Assets:A","EUR","-10","*","This is an xact note" "2012/01/08","100","With note","Liabilities:A","EUR","10","*","This is an xact note" end test ������������������������������������������ledger-3.3.2/test/baseline/cmd-echo.test������������������������������������������������������������0000664�0000000�0000000�00000000152�14411236400�0020073�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test echo foo foo end test test echo "foo bar" foo bar end test test echo "foo\nbar" foo\nbar end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-entry.test�����������������������������������������������������������0000664�0000000�0000000�00000002513�14411236400�0020321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-23 * Test 1 A $10.00 B 2012-03-24 * Test 2 ; Payee: Test 3 C 20.00 EUR D 2012-03-25 * Test 4 E 30.00 GBP F test --now 2012/03/25 entry "Test 1" 2012/03/25 Test 1 A $10.00 B end test test --now 2012/03/25 entry "Test 2" 2012/03/25 Test 2 C 20.00 EUR D end test ; I think this output is wrong, see bug #737 test --now 2012/03/25 entry "Test 3" 2012/03/25 Test 4 E 30.00 GBP F end test test --now 2012/03/25 entry "Test 4" 2012/03/25 Test 4 E 30.00 GBP F end test test entry no:such:account -> 1 __ERROR__ Error: No accounts, and no past transaction matching 'no:such:account' end test test entry --now 2012/03/25 thursday "Test 1" 2012/03/22 Test 1 A $10.00 B end test test --now 2012/03/25 entry tue at "Test 2" 2012/03/20 Test 2 C 20.00 EUR D end test test --now 2012/03/25 entry "Test 1" from D 2012/03/25 Test 1 A $10.00 D end test test --now 2012/03/25 entry "Test 1" from -> 1 __ERROR__ Error: Invalid xact command arguments end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-equity.test����������������������������������������������������������0000664�0000000�0000000�00000003515�14411236400�0020503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 GBP 2011-03-04 Buy shares Assets:Broker 2 AAA @ 0.90 GBP Assets:Bank 2011-03-05 Buy shares Assets:Broker 2 AAA @ 1.00 GBP Assets:Bank test equity 2011/03/05 Opening Balances Assets:Bank -3.80 GBP Assets:Broker 4 AAA Equity:Opening Balances -4 AAA Equity:Opening Balances 3.80 GBP end test test equity assets 2011/03/05 Opening Balances Assets:Bank -3.80 GBP Assets:Broker 4 AAA Equity:Opening Balances -4 AAA Equity:Opening Balances 3.80 GBP end test test equity assets:bank 2011/03/05 Opening Balances Assets:Bank -3.80 GBP Equity:Opening Balances end test test equity assets:broker 2011/03/05 Opening Balances Assets:Broker 4 AAA Equity:Opening Balances end test test equity --lot-prices 2011/03/05 Opening Balances Assets:Bank -3.80 GBP Assets:Broker 2 AAA {0.90 GBP} Assets:Broker 2 AAA {1.00 GBP} Equity:Opening Balances -2 AAA {0.90 GBP} Equity:Opening Balances -2 AAA {1.00 GBP} Equity:Opening Balances 3.80 GBP end test test equity --lots --date-format %Y/%m/%d 2011/03/05 Opening Balances Assets:Bank -3.80 GBP Assets:Broker 2 AAA {0.90 GBP} [2011/03/04] Assets:Broker 2 AAA {1.00 GBP} [2011/03/05] Equity:Opening Balances -2 AAA {0.90 GBP} [2011/03/04] Equity:Opening Balances -2 AAA {1.00 GBP} [2011/03/05] Equity:Opening Balances 3.80 GBP end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-payees.test����������������������������������������������������������0000664�0000000�0000000�00000000621�14411236400�0020444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011-03-01 * Z A 10 B 2011-03-02 * A C 10 D 2011-03-03 * 9 B 10 E 2011-03-04 * B B 10 E 2011-03-05 * 1 B 10 E 2011-03-06 * 2 ; Payee: 3 E 10 F test payees 1 3 9 A B Z end test test payees a Z end test test payees no:such:account end test test payees "^B$" 1 9 B Z end test ���������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-pricedb.test���������������������������������������������������������0000664�0000000�0000000�00000001255�14411236400�0020572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Opening balance A 10.00 GBP B 2012-01-02 * Test A 10.00 GBP @@ 12.00 EUR B 2012-01-03 * Test B 12.00 EUR @@ 15.80 USD C 2012-01-04 * Test C 15.80 USD @ 0.63 GBP D test pricedb P 2012/01/02 00:00:00 GBP 1.20 EUR P 2012/01/03 00:00:00 EUR 1.3166666667 USD P 2012/01/04 00:00:00 USD 0.63 GBP end test test pricedb EUR P 2012/01/03 00:00:00 EUR 1.3166666667 USD end test test pricedb GBP P 2012/01/02 00:00:00 GBP 1.20 EUR end test test pricedb USD P 2012/01/04 00:00:00 USD 0.63 GBP end test test pricedb U P 2012/01/03 00:00:00 EUR 1.3166666667 USD P 2012/01/04 00:00:00 USD 0.63 GBP end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-pricemap.test��������������������������������������������������������0000664�0000000�0000000�00000000760�14411236400�0020762�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������P 2012-03-25 EUR 0.83 GBP P 2012-03-25 EUR 1.32 $ P 2012-03-25 USD 0.75 EUR P 2012-03-25 AAA $10.00 2012-03-23 * Test 1 C 20.00 EUR @@ 16.71 GBP D 2012-03-24 * Test 2 E 30.00 GBP F 2012-03-25 * Test 3 G 1 AAA @ $10.00 H test pricemap graph G { 0[label=""]; 1[label="s"]; 2[label="%"]; 3[label="m"]; 4[label="h"]; 5[label="GBP"]; 6[label="EUR"]; 7[label="$"]; 8[label="USD"]; 9[label="AAA"]; 6--5 ; 6--7 ; 8--6 ; 9--7 ; } end test ����������������ledger-3.3.2/test/baseline/cmd-prices.test����������������������������������������������������������0000664�0000000�0000000�00000001214�14411236400�0020442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Opening balance A 10.00 GBP B 2012-01-02 * Test A 10.00 GBP @@ 12.00 EUR B 2012-01-03 * Test B 12.00 EUR @@ 15.80 USD C 2012-01-04 * Test C 15.80 USD @ 0.63 GBP D test prices 2012/01/02 GBP 1.20 EUR 2012/01/03 EUR 1.3166666667 USD 2012/01/04 USD 0.63 GBP end test test prices EUR 2012/01/03 EUR 1.3166666667 USD end test test prices USD 2012/01/04 USD 0.63 GBP end test test prices GBP 2012/01/02 GBP 1.20 EUR end test test prices U 2012/01/03 EUR 1.3166666667 USD 2012/01/04 USD 0.63 GBP end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-print.test�����������������������������������������������������������0000664�0000000�0000000�00000000315�14411236400�0020312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/12/31 Market Expenses:Food ($10,00 + $2,50) Assets:Cash test print --decimal-comma 2008/12/31 Market Expenses:Food ($10,00 + $2,50) Assets:Cash end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-register.test��������������������������������������������������������0000664�0000000�0000000�00000003323�14411236400�0021004�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-10 * Phone expense on holidays Expenses:Phone 12.00 EUR @@ 10.00 GBP Assets:Cash -10.00 GBP 2012-01-31 * Rent expense Expenses:Rent 550.00 GBP Assets:Cash -550.00 GBP 2012-02-01 * Buy AAA Assets:Investment 1 AAA @ 10.00 GBP Assets:Cash -10.00 GBP test reg 12-Jan-10 Phone expense on ho.. Expenses:Phone 12.00 EUR 12.00 EUR Assets:Cash -10.00 GBP 12.00 EUR -10.00 GBP 12-Jan-31 Rent expense Expenses:Rent 550.00 GBP 12.00 EUR 540.00 GBP Assets:Cash -550.00 GBP 12.00 EUR -10.00 GBP 12-Feb-01 Buy AAA Assets:Investment 1 AAA 1 AAA 12.00 EUR -10.00 GBP Assets:Cash -10.00 GBP 1 AAA 12.00 EUR -20.00 GBP end test test r :inve 12-Feb-01 Buy AAA Assets:Investment 1 AAA 1 AAA end test test reg :inve 12-Feb-01 Buy AAA Assets:Investment 1 AAA 1 AAA end test test register :inve 12-Feb-01 Buy AAA Assets:Investment 1 AAA 1 AAA end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-script.test����������������������������������������������������������0000664�0000000�0000000�00000000625�14411236400�0020466�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test eval 'foo(w, u)=(z=w+u;z*2); (a=1 + 1; foo(10, 15))' 50 end test test eval 'foo(x, y, z)=print(x, y, z); bar(x)=x; foo(1, 2, 3); bar(3)' 123 3 end test test eval 'total_expr=$100;amount_expr=$15;x=total_expr;x=x/5;x=amount_expr-x*5;x' $-85 end test test eval 'foo = x, y, z -> print(x, y, z); foo(1, 2, 3)' 123 1 end test test eval 'foo(x,y)=y(1, 2, 3);foo(amount_expr, (s,d,t -> t))' 3 end test �����������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-select.test����������������������������������������������������������0000664�0000000�0000000�00000004673�14411236400�0020450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-02-28 * Test 1 E 20.00 EUR F 2012-02-29 * Test 2 Test 10.01 EUR F 2012-03-24 Test 3 C 30.00 EUR D 2012-03-25 (test) Test 4 ; Payee: Test 5 E 40.00 GBP F test select "date, account, amount" from posts 12-Feb-28 E 20.00 EUR 12-Feb-28 F -20.00 EUR 12-Feb-29 Test 10.01 EUR 12-Feb-29 F -10.01 EUR 12-Mar-24 C 30.00 EUR 12-Mar-24 D -30.00 EUR 12-Mar-25 E 40.00 GBP 12-Mar-25 F -40.00 GBP end test test select "date, account, amount from posts where account =~ /^e/" 12-Feb-28 E 20.00 EUR 12-Mar-25 E 40.00 GBP end test test select "date, account, amount from posts where account =~ /e/" 12-Feb-28 E 20.00 EUR 12-Feb-29 Test 10.01 EUR 12-Mar-25 E 40.00 GBP end test ; leave out "from posts" since it is the default test select "date, account, amount where account =~ /e/" 12-Feb-28 E 20.00 EUR 12-Feb-29 Test 10.01 EUR 12-Mar-25 E 40.00 GBP end test test select "date, payee, amount from posts where account =~ /e/ and commodity =~ /GBP/" 12-Mar-25 Test 5 40.00 GBP end test test select "date, payee, amount * 2 from posts where account =~ /e/ and commodity =~ /GBP/" 12-Mar-25 Test 5 80.00 GBP end test test select "date, code, amount from posts where account =~ /e/ and commodity =~ /GBP/" 12-Mar-25 test 40.00 GBP end test test select "date, code * 2, amount from posts where account =~ /e/ and commodity =~ /GBP/" 12-Mar-25 testtest 40.00 GBP end test ���������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-source.test����������������������������������������������������������0000664�0000000�0000000�00000002667�14411236400�0020472�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ xxx 2012-02-28 * Test E 30.00 EUR F G 2012-03-24 Test C 30.00 EUR D C 2012/03/xx E 30.00 EUR F 2012-03-25 * Test G AAA H 2012-03-26 * Test I 1,00.00 EUR J -100.00 EUR 2012-03-27 * Test K 100.00 EUR L -200.00 EUR test source -> 7 __ERROR__ While parsing file "$FILE", line 1: While parsing periodic transaction: > ~ xxx Error: Unexpected date period token 'xxx' While parsing file "$FILE", line 6: Error: Only one posting with null amount allowed per transaction While parsing file "$FILE", line 11: Error: Only one posting with null amount allowed per transaction While parsing file "$FILE", line 13: While parsing transaction: > 2012/03/xx Error: Invalid date: 2012/03/xx While parsing file "$FILE", line 18: While parsing posting: G AAA ^^^ Error: No quantity specified for amount While parsing file "$FILE", line 22: While parsing posting: I 1,00.00 EUR ^^^^^^^^^^^ Error: Incorrect use of thousand-mark comma While parsing file "$FILE", line 27: While balancing transaction from "$FILE", lines 25-27: > 2012-03-27 * Test > K 100.00 EUR > L -200.00 EUR Unbalanced remainder is: -100.00 EUR Amount to balance against: 100.00 EUR Error: Transaction does not balance end test �������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-stats.test�����������������������������������������������������������0000664�0000000�0000000�00000001144�14411236400�0020315�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2012-02-28 * Test E 30.00 EUR F 2012-02-29 * Test E 30.00 EUR F 2012-03-24 Test A 30.00 EUR B test stats --now "2012-03-31" Time period: 12-Feb-28 to 12-Mar-24 (25 days) Files these postings came from: $sourcepath/test/baseline/cmd-stats.test Unique payees: 1 Unique accounts: 4 Number of postings: 6 (0.24 per day) Uncleared postings: 2 Days since last post: 7 Posts in last 7 days: 2 Posts in last 30 days: 2 Posts seen this month: 2 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-stats1.test����������������������������������������������������������0000664�0000000�0000000�00000001034�14411236400�0020374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������= /^Income:/ (Liabilities:Tithe) 0.10 ~ Monthly Income:Interest:Bank Interest -$960.00 Equity test stats --now "2023-02-06" Time period: 23-Feb-06 to 23-Feb-06 (0 days) Files these postings came from: $FILE Unique payees: 1 Unique accounts: 1 Number of postings: 1 (inf per day) Uncleared postings: 1 Days since last post: 0 Posts in last 7 days: 1 Posts in last 30 days: 1 Posts seen this month: 1 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-tags.test������������������������������������������������������������0000664�0000000�0000000�00000001135�14411236400�0020115�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2014-01-01 * Test 1 A $10 ; :bar: B ; :foo: 2014-01-02 * Test 2 A $10 ; :a: B 2014-01-03 * Test 3 A $10 B ; :b: 2014-01-03 * Test 4 ; :xxx: A $10 B 2014-01-03 * Test 5 A $10 ; a: aaa B 2014-02-04 * Test 6 A $10 ; a: aaa B 2014-01-01 * Test 7 A $10 B ; b: bbb test tags a b bar foo xxx end test test tags --values a a: aaa b b: bbb bar foo xxx end test test tags --values --count 1 a 2 a: aaa 1 b 1 b: bbb 1 bar 1 foo 2 xxx end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/cmd-xact.test������������������������������������������������������������0000664�0000000�0000000�00000001522�14411236400�0020116�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-23 * Test 1 A $10.00 B 2012-03-24 * Test 2 ; Payee: Test 3 C 20.00 EUR D 2012-03-25 * Test 4 E 30.00 GBP F test --now 2012/03/25 xact "Test 1" 2012/03/25 Test 1 A $10.00 B end test test --now 2012/03/25 xact "Test 2" 2012/03/25 Test 2 C 20.00 EUR D end test ; I think this output is wrong, see bug #737 test --now 2012/03/25 xact "Test 3" 2012/03/25 Test 4 E 30.00 GBP F end test test --now 2012/03/25 xact "Test 4" 2012/03/25 Test 4 E 30.00 GBP F end test test xact no:such:account -> 1 __ERROR__ Error: No accounts, and no past transaction matching 'no:such:account' end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-account.test���������������������������������������������������������0000664�0000000�0000000�00000002476�14411236400�0020637�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ commodity $ format $1,000.00 account Assets:Cash check abs(amount) <= 20 check commodity == '$' default account Expenses:Food alias food payee KFC 2012-02-27 KFC Expenses:Unknown $20.00 Assets:Cash 2012-02-28 KFC food $20.00 Assets:Cash 2012-02-29 KFC food $25.00 Assets:Cash 2012-02-29 KFC food $25.00 Assets:Cash test reg 12-Feb-27 KFC Expenses:Food $20.00 $20.00 Assets:Cash $-20.00 0 12-Feb-28 KFC Expenses:Food $20.00 $20.00 Assets:Cash $-20.00 0 12-Feb-29 KFC Expenses:Food $25.00 $25.00 Assets:Cash $-25.00 0 12-Feb-29 KFC Expenses:Food $25.00 $25.00 Assets:Cash $-25.00 0 __ERROR__ Warning: "$FILE", line 24: Transaction check failed: (abs(amount) <= {20}) Warning: "$FILE", line 28: Transaction check failed: (abs(amount) <= {20}) end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-alias-fail.test������������������������������������������������������0000664�0000000�0000000�00000000232�14411236400�0021171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������alias Foo=Foo 2011-01-01 Test Foo 10 EUR Bar test source -> 1 __ERROR__ While parsing file "$FILE", line 1: Error: Illegal alias Foo=Foo end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-alias.test�����������������������������������������������������������0000664�0000000�0000000�00000000735�14411236400�0020270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������alias A=B:A alias B=C:B alias C=D:C account Delta alias D 2001-01-01 Test A 10 EUR Foo 2001-01-01 Test D 20 EUR Foo test reg 01-Jan-01 Test B:A 10 EUR 10 EUR Foo -10 EUR 0 01-Jan-01 Test Delta 20 EUR 20 EUR Foo -20 EUR 0 end test �����������������������������������ledger-3.3.2/test/baseline/dir-apply.dat������������������������������������������������������������0000664�0000000�0000000�00000000104�14411236400�0020103�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-12 KFC Expenses:Food $40 Assets:Cash ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-apply.test�����������������������������������������������������������0000664�0000000�0000000�00000001337�14411236400�0020323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������apply account Master Account 2012-03-12 KFC Expenses:Food $20 Assets:Cash end apply account apply account Master Account 2012-03-12 KFC Expenses:Food $20 Assets:Cash end apply apply account Master Account 2012-03-12 KFC Expenses:Food $20 Assets:Cash end apply account Master Account include dir-apply.dat end test reg food 12-Mar-12 KFC Master A:Expenses:Food $20 $20 12-Mar-12 KFC Master A:Expenses:Food $20 $40 12-Mar-12 KFC Master A:Expenses:Food $20 $60 12-Mar-12 KFC Master A:Expenses:Food $40 $100 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-commodity-alias.test�������������������������������������������������0000664�0000000�0000000�00000000621�14411236400�0022264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������commodity $ alias USD 2012-03-12 * $ A $10.00 B 2012-03-12 * USD A 15.00 USD B test bal 25.00 $ A -25.00 $ B -------------------- 0 end test test reg a 12-Mar-12 $ A 10.00 $ 10.00 $ 12-Mar-12 USD A 15.00 $ 25.00 $ end test ���������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-commodity-value.test�������������������������������������������������0000664�0000000�0000000�00000001106�14411236400�0022306�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������commodity $ value 10 EUR commodity USD alias FOO value 25 EUR 2012-03-06 KFC Expenses:Food $20.00 Assets:Cash 2012-03-08 KFC Expenses:Food USD 750,00 Assets:Cash 2012-03-10 KFC Expenses:Food USD 750,00 Assets:Cash test reg food -X EUR --now=2012-03-15 12-Mar-06 KFC Expenses:Food 200 EUR 200 EUR 12-Mar-08 KFC Expenses:Food 18750 EUR 18950 EUR 12-Mar-10 KFC Expenses:Food 18750 EUR 37700 EUR end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-commodity.test�������������������������������������������������������0000664�0000000�0000000�00000000477�14411236400�0021206�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������account A account B commodity GBP 2012-03-25 GBP A 10.00 GBP B 2012-03-26 EUR A 20.00 EUR B test bal --pedantic -> 1 __ERROR__ While parsing file "$FILE", line 10: While parsing posting: A 20.00 EUR ^^^^^^^^^ Error: Unknown commodity 'EUR' end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-fixed.test�����������������������������������������������������������0000664�0000000�0000000�00000000473�14411236400�0020275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������apply fixed XCD $0.374531835206 2008/04/08 KFC Expenses:Food XCD 43.00 Assets:Cash end apply fixed test reg 08-Apr-08 KFC Expenses:Food XCD 43.00 XCD 43.00 Assets:Cash XCD -43.00 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-import_py.test�������������������������������������������������������0000664�0000000�0000000�00000001443�14411236400�0021216�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os tag PATH check os.path.isfile(value) 2012-02-29 KFC ; PATH: test/baseline/dir-import_py.test Expenses:Food $20 Assets:Cash 2012-02-29 KFC ; PATH: test/baseline/dir-import_noexist.test Expenses:Food $20 Assets:Cash test reg 12-Feb-29 KFC Expenses:Food $20 $20 Assets:Cash $-20 0 12-Feb-29 KFC Expenses:Food $20 $20 Assets:Cash $-20 0 __ERROR__ Warning: "$sourcepath/test/baseline/dir-import_py.test", line 14: Metadata check failed for (PATH: test/baseline/dir-import_noexist.test): ((os.path).isfile(value)) end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-payee.test�����������������������������������������������������������0000664�0000000�0000000�00000001611�14411236400�0020274�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������payee KFC alias Kentucky Fried Chicken payee Foo Bar Inc uuid 2a2e21d434356f886c84371eebac6e44f1337fda 2012-03-25 * Kentucky Fried Chicken A 10 B 2012-03-26 * KFC A 10 ; Payee: Kentucky Fried Chicken B 2014-05-13 * UNHELPFUL PAYEE ; will be read as being 'Foo Bar Inc' ; UUID: 2a2e21d434356f886c84371eebac6e44f1337fda A 20 B test reg 12-Mar-25 KFC A 10 10 B -10 0 12-Mar-26 KFC A 10 10 B -10 0 14-May-13 Foo Bar Inc A 20 20 B -20 0 end test �����������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-python_py.test�������������������������������������������������������0000664�0000000�0000000�00000002051�14411236400�0021221�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������python import os def check_path(path): return os.path.isfile(path) tag PATH check check_path(value) check os.path.isfile(value) 2012-02-29 KFC ; PATH: test/baseline/feat-import_py.test Expenses:Food $20 Assets:Cash 2012-02-29 KFC ; PATH: test/baseline/feat-import_noexist.test Expenses:Food $20 Assets:Cash test reg 12-Feb-29 KFC Expenses:Food $20 $20 Assets:Cash $-20 0 12-Feb-29 KFC Expenses:Food $20 $20 Assets:Cash $-20 0 __ERROR__ Warning: "$sourcepath/test/baseline/dir-python_py.test", line 18: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) Warning: "$sourcepath/test/baseline/dir-python_py.test", line 18: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): ((os.path).isfile(value)) end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/dir-tag.test�������������������������������������������������������������0000664�0000000�0000000�00000001327�14411236400�0017750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tag Happy check value == 'Valley' 2012-02-27 * KFC ; Happy: Valley Expenses:Unknown $20.00 ; Happy: Summer Assets:Cash 2012-02-28 * KFC food $20.00 Assets:Cash test reg 12-Feb-27 KFC Expenses:Unknown $20.00 $20.00 Assets:Cash $-20.00 0 12-Feb-28 KFC food $20.00 $20.00 Assets:Cash $-20.00 0 __ERROR__ Warning: "$sourcepath/test/baseline/dir-tag.test", line 8: Metadata check failed for (Happy: Summer): (value == "Valley") end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-annotations.test����������������������������������������������������0000664�0000000�0000000�00000002034�14411236400�0021667�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-09 KFC Expenses:Food 10 CHIK @ $50 Assets:Cash 2012-03-09 KFC Assets:Cash $75 Expenses:Food -10 CHIK {{$50}} @ $75 Equity:Capital Gains $-25 2012-03-09 KFC Expenses:Food 10 CHIK Assets:Cash $-50 2012-03-09 KFC Assets:Cash $75 Expenses:Food -10 CHIK {{$50}} Equity:Capital Gains $-25 test print 2012/03/09 KFC Expenses:Food 10 CHIK @ $50 Assets:Cash 2012/03/09 KFC Assets:Cash $75 Expenses:Food -10 CHIK {$5} @ $75 Equity:Capital Gains $-25 2012/03/09 KFC Expenses:Food 10 CHIK Assets:Cash $-50 2012/03/09 KFC Assets:Cash $75 Expenses:Food -10 CHIK {$5} Equity:Capital Gains $-25 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-balance-assignments.test��������������������������������������������0000664�0000000�0000000�00000001464�14411236400�0023256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/01/01 Entry Assets:Cash $10,000.00 Equity:Opening Balances 2009/02/01 Entry Expenses:Cash $100.00 Assets:Cash 2009/02/02 Entry Expenses:Cash $100.00 Assets:Cash 2009/02/03 Entry Expenses:Cash $100.00 Assets:Cash $-100.00 = $9,700.00 2009/02/04 Entry Expenses:Cash $100.00 Assets:Cash $-100.00 = $9,600.00 2009/02/05 Entry Expenses:Cash $100.00 Assets:Cash 2009/02/05 Entry Expenses:Cash Assets:Cash = ($4,000.00 + $100.00) test bal $4,100.00 Assets:Cash $-10,000.00 Equity:Opening Balances $5,900.00 Expenses:Cash -------------------- 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-balance_assert-off.test���������������������������������������������0000664�0000000�0000000�00000000620�14411236400�0023047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2014-05-01 * Opening balance Assets:Cash $100 Equity:Opening balance 2014-05-10 * Spend money Expenses:Foo $10 Assets:Cash -$10 = $80 test bal -> 1 __ERROR__ While parsing file "$FILE", line 8: While parsing posting: Assets:Cash -$10 = $80 ^^^ Error: Balance assertion off by $-10 (expected to see $90) end test ����������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-balance_assert.test�������������������������������������������������0000664�0000000�0000000�00000000402�14411236400�0022275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 Opening Balance Assets:Checking $100 Equity 2012-01-01 Reconciliation [Assets:Checking] = $100 test balance $100 Assets:Checking $-100 Equity -------------------- 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-balance_assert_split.test�������������������������������������������0000664�0000000�0000000�00000002200�14411236400�0023506�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; a.dat 2012-01-01 Test Expenses:Unknown $100.00 Liabilities:MasterCard 2012-01-02 Test Expenses:Unknown $100.00 Liabilities:MasterCard 2012-01-03 Test Expenses:Unknown $100.00 Liabilities:MasterCard 2012-01-04 Test ; UUID: foo Liabilities:MasterCard $150.00 = $-150 <Assets:Checking> 2012-01-04 Test ; UUID: bar Liabilities:MasterCard $150.00 = $0 <Assets:Checking> 2012-01-04 Test ; UUID: baz Liabilities:MasterCard $150.00 = $150 <Assets:Checking> ;; b.dat 2012-01-01 Test Assets:Checking $150.00 Income 2012-01-02 Test Assets:Checking $150.00 Income 2012-01-03 Test Assets:Checking $150.00 Income 2012-01-04 Test ; UUID: foo Liabilities:MasterCard $150.00 Assets:Checking $-150.00 = $300.00 2012-01-04 Test ; UUID: bar Liabilities:MasterCard $150.00 Assets:Checking $-150.00 = $150.00 test balance $300.00 Expenses:Unknown $-450.00 Income $150.00 Liabilities:MasterCard -------------------- 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-check.test����������������������������������������������������������0000664�0000000�0000000�00000001034�14411236400�0020406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������= /Checking/ check account =~ /Foo/ 2010-06-24 Sample Expenses:Food $100 Assets:Checking check account("Assets:Checking").all(account =~ /Expense/) test bal $-100 Assets:Checking $100 Expenses:Food -------------------- 0 __ERROR__ Warning: "$sourcepath/test/baseline/feat-check.test", line 6: Transaction check failed: (account =~ /Foo/) Warning: "$sourcepath/test/baseline/feat-check.test", line 8: Check failed: account("Assets:Checking").all(account =~ /Expense/) end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-convert-with-directives.dat�����������������������������������������0000664�0000000�0000000�00000000123�14411236400�0023710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,payee,amount 2012/01/01,KFC,$10 2012/01/02,"REWE SAGT DANKE 123454321",10€ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-convert-with-directives.test����������������������������������������0000664�0000000�0000000�00000001333�14411236400�0024123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������account Expenses:Food payee KFC payee REWE payee REWE alias REWE SAGT DANKE # When reading csv file without directives: test -f /dev/null convert test/baseline/feat-convert-with-directives.dat 2012/01/01 * KFC Expenses:Unknown $10 Equity:Unknown 2012/01/02 * REWE SAGT DANKE 123454321 Expenses:Unknown 10€ Equity:Unknown end test # When reading csv file with directives: test --account "Assets:Cash" convert test/baseline/feat-convert-with-directives.dat 2012/01/01 * KFC Expenses:Food $10 Assets:Cash 2012/01/02 * REWE Expenses:Food 10€ Assets:Cash end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-fixated-prices.test�������������������������������������������������0000664�0000000�0000000�00000001333�14411236400�0022242�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������P 1989/01/15 12:00:00 GAL $3 1990/01/01 Payee Expenses:Gas 100 GAL {=$2} Liabilities:MasterCard $-200 P 1990/01/15 12:00:00 GAL $3 1990/02/01 Payee Expenses:Gas 100 FOO {$2} Liabilities:MasterCard $-200 P 1990/02/15 12:00:00 FOO $3 1990/02/20 Payee Expenses:Gas 100 FOO Liabilities:MasterCard test reg -V gas 90-Jan-01 Payee Expenses:Gas $200 $200 90-Feb-01 Payee Expenses:Gas $200 $400 90-Feb-20 Commodities revalued <Revalued> $100 $500 90-Feb-20 Payee Expenses:Gas $300 $800 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-import_py.test������������������������������������������������������0000664�0000000�0000000�00000001450�14411236400�0021355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--import featimport.py tag PATH check check_path(value) 2012-02-29 KFC ; PATH: test/baseline/feat-import_py.test Expenses:Food $20 Assets:Cash 2012-02-29 KFC ; PATH: test/baseline/feat-import_noexist.test Expenses:Food $20 Assets:Cash test reg 12-Feb-29 KFC Expenses:Food $20 $20 Assets:Cash $-20 0 12-Feb-29 KFC Expenses:Food $20 $20 Assets:Cash $-20 0 __ERROR__ Warning: "$sourcepath/test/baseline/feat-import_py.test", line 14: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-option_py.test������������������������������������������������������0000664�0000000�0000000�00000000445�14411236400�0021356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������python def option_pyfirst(context): print("In --pyfirst (from %s)" % context) def option_pysecond(context, val): print("In --pysecond=%s (from %s)" % (val, context)) --pyfirst --pysecond Hey test reg In --pyfirst (from $FILE) In --pysecond=Hey (from $FILE) end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-value-expr.test�����������������������������������������������������0000664�0000000�0000000�00000006131�14411236400�0021424�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; A valuation function receives three arguments: ;; ;; 'source' A string identifying the commodity whose price ;; is being asked for (example: "EUR") ;; ;; 'date' The reference date the price should be relative. ;; ;; 'target' A string identifying the "target" commodity, or ;; the commodity the returned price should be in. ;; This argument is null if -V was used instead of -X. ;; ;; The valuation function should return an amount. If you've written your ;; function in Python, you can return something like Amount("$100"). If the ;; function returns an explicit value, that value is always used, regardless ;; of the commodity, the date, or the desired target commodity. define myfunc_seven(s, d, t) = 7 EUR ;; In order to specific a fixed price, but still valuate that price into the ;; target commodity, use something like this: define myfunc_five(s, d, t) = market(5 EUR, d, t) ;; The 'value' directive sets the valuation used for all commodities used in ;; the rest of the daat stream. This is the fallback, if nothing more ;; specific is found. value myfunc_seven ;; You can set a specific valuation function on a per-commodity basis. ;; Instead of defining a function, you can also pass a lambda. commodity $ value s, d, t -> 6 EUR ;; Each account can also provide a default valuation function for any ;; commodities transferred to that account. account Expenses:Food5 value myfunc_five ;; The metadata field "Value", if found, overrides the valuation function on a ;; transaction-wide or per-posting basis. = @XACT and Food ; Value:: 8 EUR (Equity) $1 = @POST and Dining (Expenses:Food9) $1 ; Value:: 9 EUR ;; Lastly, you can specify the valuation function/value for any specific ;; amount using the (( )) commodity annotation. 2012-03-02 KFC Expenses:Food2 $1 ((2 EUR)) Assets:Cash2 2012-03-03 KFC Expenses:Food3 $1 ; Value:: 3 EUR Assets:Cash3 2012-03-04 KFC ; Value:: 4 EUR Expenses:Food4 $1 Assets:Cash4 2012-03-05 KFC Expenses:Food5 $1 Assets:Cash5 2012-03-06 KFC Expenses:Food6 $1 Assets:Cash6 2012-03-07 KFC Expenses:Food7 1 CAD Assets:Cash7 2012-03-08 XACT Expenses:Food8 $1 Assets:Cash8 2012-03-09 POST Expenses:Dining9 $1 Assets:Cash9 test reg -V food 12-Mar-02 KFC Expenses:Food2 2 EUR 2 EUR 12-Mar-03 KFC Expenses:Food3 3 EUR 5 EUR 12-Mar-04 KFC Expenses:Food4 4 EUR 9 EUR 12-Mar-05 KFC Expenses:Food5 5 EUR 14 EUR 12-Mar-06 KFC Expenses:Food6 6 EUR 20 EUR 12-Mar-07 KFC Expenses:Food7 7 EUR 27 EUR 12-Mar-08 XACT Expenses:Food8 8 EUR 35 EUR 12-Mar-09 POST (Expenses:Food9) 9 EUR 44 EUR end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/feat-value_py2.test������������������������������������������������������0000664�0000000�0000000�00000000757�14411236400�0021252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������python def print_type(val): print(type(val), val) eval print_type(true) eval print_type([2010/08/10]) eval print_type(10) eval print_type($10.00) eval print_type($10.00 + CAD 30) eval print_type("Hello!") eval print_type(/Hello!/) ;eval print_type((1, 2, 3)) test reg <type 'bool'> True <type 'datetime.date'> 2010-08-10 <class 'ledger.Amount'> 10 <class 'ledger.Amount'> $10.00 <class 'ledger.Balance'> $10.00 CAD 30 <type 'unicode'> Hello! <class 'ledger.Value'> Hello! end test �����������������ledger-3.3.2/test/baseline/feat-value_py3.test������������������������������������������������������0000664�0000000�0000000�00000000756�14411236400�0021252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������python def print_type(val): print(type(val), val) eval print_type(true) eval print_type([2010/08/10]) eval print_type(10) eval print_type($10.00) eval print_type($10.00 + CAD 30) eval print_type("Hello!") eval print_type(/Hello!/) ;eval print_type((1, 2, 3)) test reg <class 'bool'> True <class 'datetime.date'> 2010-08-10 <class 'ledger.Amount'> 10 <class 'ledger.Amount'> $10.00 <class 'ledger.Balance'> $10.00 CAD 30 <class 'str'> Hello! <class 'ledger.Value'> Hello! end test ������������������ledger-3.3.2/test/baseline/featimport.py������������������������������������������������������������0000664�0000000�0000000�00000000122�14411236400�0020234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os def check_path(path_value): return os.path.isfile(str(path_value)) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/featoption.py������������������������������������������������������������0000664�0000000�0000000�00000000247�14411236400�0020242�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def option_pyfirst(context): print "In --pyfirst (from %s)" % context def option_pysecond(context, val): print "In --pysecond=%sh (from %s)" % (val, context) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-abbrev-len.test������������������������������������������������������0000664�0000000�0000000�00000000626�14411236400�0021237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --abbrev-len=4 07-Feb-02 RD VMMXX Asse:Inve:Vangua:VMMXX 0.350 VMMXX 0.350 VMMXX Inco:Divi:Vangua:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ����������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-account-width.test���������������������������������������������������0000664�0000000�0000000�00000000720�14411236400�0021766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --account-width=40 07-Feb-02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX 0.350 VMMXX Income:Dividends:Vanguard:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ������������������������������������������������ledger-3.3.2/test/baseline/opt-account.test���������������������������������������������������������0000664�0000000�0000000�00000000653�14411236400�0020656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --account='payee + ":" + commodity' 07-Feb-02 RD VMMXX RD:VM:As:In:Vang:VMMXX 0.350 VMMXX 0.350 VMMXX 07-Feb-02 RD VMMXX RD:$:In:Di:Vangu:VMMXX $-0.35 $-0.35 0.350 VMMXX end test �������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-actual.test����������������������������������������������������������0000664�0000000�0000000�00000000503�14411236400�0020465�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������= Books Expenses:Taxes 0.05 Assets:Checking -0.05 2008/01/01 January Expenses:Books $10.00 Assets:Cash $-10.00 test print --actual 2008/01/01 January Expenses:Books $10.00 Assets:Cash $-10.00 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-add-budget.test������������������������������������������������������0000664�0000000�0000000�00000035322�14411236400�0021223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ monthly Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash test reg --add-budget books cards --now=2009/12/31 08-Jan-01 Budget transaction Expenses:Books $-10.00 $-10.00 08-Jan-01 January Expenses:Books $10.00 0 Liabilities:Cards $10.00 $10.00 08-Jan-31 End of January Expenses:Books $10.00 $20.00 Liabilities:Cards $10.00 $30.00 08-Feb-01 Budget transaction Expenses:Books $-10.00 $20.00 08-Feb-01 February Expenses:Books $20.00 $40.00 Liabilities:Cards $20.00 $60.00 08-Feb-28 End of February Expenses:Books $20.00 $80.00 Liabilities:Cards $20.00 $100.00 08-Mar-01 Budget transaction Expenses:Books $-10.00 $90.00 08-Mar-01 March Expenses:Books $30.00 $120.00 Liabilities:Cards $30.00 $150.00 08-Mar-31 End of March Expenses:Books $30.00 $180.00 Liabilities:Cards $30.00 $210.00 08-Apr-01 Budget transaction Expenses:Books $-10.00 $200.00 08-Apr-01 April Expenses:Books $40.00 $240.00 Liabilities:Cards $40.00 $280.00 08-Apr-30 End of April Expenses:Books $40.00 $320.00 Liabilities:Cards $40.00 $360.00 08-May-01 Budget transaction Expenses:Books $-10.00 $350.00 08-May-01 May Expenses:Books $50.00 $400.00 Liabilities:Cards $50.00 $450.00 08-May-31 End of May Expenses:Books $50.00 $500.00 Liabilities:Cards $50.00 $550.00 08-Jun-01 Budget transaction Expenses:Books $-10.00 $540.00 08-Jun-01 June Expenses:Books $60.00 $600.00 Liabilities:Cards $60.00 $660.00 08-Jun-30 End of June Expenses:Books $60.00 $720.00 Liabilities:Cards $60.00 $780.00 08-Jul-01 Budget transaction Expenses:Books $-10.00 $770.00 08-Jul-01 July Expenses:Books $70.00 $840.00 Liabilities:Cards $70.00 $910.00 08-Jul-31 End of July Expenses:Books $70.00 $980.00 Liabilities:Cards $70.00 $1050.00 08-Aug-01 Budget transaction Expenses:Books $-10.00 $1040.00 08-Aug-01 August Expenses:Books $80.00 $1120.00 Liabilities:Cards $80.00 $1200.00 08-Aug-31 End of August Expenses:Books $80.00 $1280.00 Liabilities:Cards $80.00 $1360.00 08-Sep-01 Budget transaction Expenses:Books $-10.00 $1350.00 08-Sep-01 September Expenses:Books $90.00 $1440.00 Liabilities:Cards $90.00 $1530.00 08-Sep-30 End of September Expenses:Books $90.00 $1620.00 Liabilities:Cards $90.00 $1710.00 08-Oct-01 Budget transaction Expenses:Books $-10.00 $1700.00 08-Oct-01 October Expenses:Books $100.00 $1800.00 Liabilities:Cards $100.00 $1900.00 08-Oct-31 End of October Expenses:Books $100.00 $2000.00 Liabilities:Cards $100.00 $2100.00 08-Nov-01 Budget transaction Expenses:Books $-10.00 $2090.00 08-Nov-01 November Expenses:Books $110.00 $2200.00 Liabilities:Cards $110.00 $2310.00 08-Nov-30 End of November Expenses:Books $110.00 $2420.00 Liabilities:Cards $110.00 $2530.00 08-Dec-01 Budget transaction Expenses:Books $-10.00 $2520.00 08-Dec-01 December Expenses:Books $120.00 $2640.00 Liabilities:Cards $120.00 $2760.00 08-Dec-31 End of December Expenses:Books $120.00 $2880.00 Liabilities:Cards $120.00 $3000.00 09-Jan-01 Budget transaction Expenses:Books $-10.00 $2990.00 09-Jan-01 January Expenses:Books $10.00 $3000.00 Liabilities:Cards $10.00 $3010.00 09-Jan-31 End of January Expenses:Books $10.00 $3020.00 Liabilities:Cards $10.00 $3030.00 09-Feb-01 Budget transaction Expenses:Books $-10.00 $3020.00 09-Feb-01 February Expenses:Books $20.00 $3040.00 Liabilities:Cards $20.00 $3060.00 09-Feb-28 End of February Expenses:Books $20.00 $3080.00 Liabilities:Cards $20.00 $3100.00 09-Mar-01 Budget transaction Expenses:Books $-10.00 $3090.00 09-Mar-01 March Expenses:Books $30.00 $3120.00 Liabilities:Cards $30.00 $3150.00 09-Mar-31 End of March Expenses:Books $30.00 $3180.00 Liabilities:Cards $30.00 $3210.00 09-Apr-01 Budget transaction Expenses:Books $-10.00 $3200.00 09-Apr-01 April Expenses:Books $40.00 $3240.00 Liabilities:Cards $40.00 $3280.00 09-Apr-30 End of April Expenses:Books $40.00 $3320.00 Liabilities:Cards $40.00 $3360.00 09-May-01 Budget transaction Expenses:Books $-10.00 $3350.00 09-May-01 May Expenses:Books $50.00 $3400.00 Liabilities:Cards $50.00 $3450.00 09-May-31 End of May Expenses:Books $50.00 $3500.00 Liabilities:Cards $50.00 $3550.00 09-Jun-01 Budget transaction Expenses:Books $-10.00 $3540.00 09-Jun-01 June Expenses:Books $60.00 $3600.00 Liabilities:Cards $60.00 $3660.00 09-Jun-30 End of June Expenses:Books $60.00 $3720.00 Liabilities:Cards $60.00 $3780.00 09-Jul-01 Budget transaction Expenses:Books $-10.00 $3770.00 09-Jul-01 July Expenses:Books $70.00 $3840.00 Liabilities:Cards $70.00 $3910.00 09-Jul-31 End of July Expenses:Books $70.00 $3980.00 Liabilities:Cards $70.00 $4050.00 09-Aug-01 Budget transaction Expenses:Books $-10.00 $4040.00 09-Aug-01 August Expenses:Books $80.00 $4120.00 Liabilities:Cards $80.00 $4200.00 09-Aug-31 End of August Expenses:Books $80.00 $4280.00 Liabilities:Cards $80.00 $4360.00 09-Sep-01 Budget transaction Expenses:Books $-10.00 $4350.00 09-Sep-01 September Expenses:Books $90.00 $4440.00 Liabilities:Cards $90.00 $4530.00 09-Sep-30 End of September Expenses:Books $90.00 $4620.00 Liabilities:Cards $90.00 $4710.00 09-Oct-01 Budget transaction Expenses:Books $-10.00 $4700.00 09-Oct-01 October Expenses:Books $100.00 $4800.00 Liabilities:Cards $100.00 $4900.00 09-Oct-31 End of October Expenses:Books $100.00 $5000.00 Liabilities:Cards $100.00 $5100.00 09-Nov-01 Budget transaction Expenses:Books $-10.00 $5090.00 09-Nov-01 November Expenses:Books $110.00 $5200.00 Liabilities:Cards $110.00 $5310.00 09-Nov-30 End of November Expenses:Books $110.00 $5420.00 Liabilities:Cards $110.00 $5530.00 09-Dec-01 Budget transaction Expenses:Books $-10.00 $5520.00 09-Dec-01 December Expenses:Books $120.00 $5640.00 Liabilities:Cards $120.00 $5760.00 09-Dec-31 End of December Expenses:Books $120.00 $5880.00 Liabilities:Cards $120.00 $6000.00 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-amount-data.test�����������������������������������������������������0000664�0000000�0000000�00000000303�14411236400�0021424�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --amount-data 2007-02-02 0.35 2007-02-02 -0.35 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-amount-width.test����������������������������������������������������0000664�0000000�0000000�00000000703�14411236400�0021636�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --amount-width=18 07-Feb-02 RD VMMXX As:Investm:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Dividen:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test �������������������������������������������������������������ledger-3.3.2/test/baseline/opt-amount.test����������������������������������������������������������0000664�0000000�0000000�00000000502�14411236400�0020516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --amount=10 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 10 10 In:Divid:Vanguar:VMMXX 10 20 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-auto-match.dat�������������������������������������������������������0000664�0000000�0000000�00000000121�14411236400�0021043�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,amount,desc, 2012/03/01,10,Food, 2012/03/02,10,Phone, 2012/03/02,10,Dining, �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-auto-match.test������������������������������������������������������0000664�0000000�0000000�00000003550�14411236400�0021263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Opening balance Assets:Cash 100.00 EUR Equity:Opening Balance 2012-01-02 * Food Expenses:Food 25.00 EUR Assets:Cash -25.00 EUR 2012-01-03 * Phone Expenses:Phone 10.00 EUR Assets:Cash -10.00 EUR 2012-01-04 * Dining Expenses:Food 20.00 EUR Liabilities:CC -20.00 EUR test --input-date-format "%Y-%m-%d" convert test/baseline/opt-auto-match.dat 2012/03/01 * Food Expenses:Unknown 10 Equity:Unknown 2012/03/02 * Phone Expenses:Unknown 10 Equity:Unknown 2012/03/02 * Dining Expenses:Unknown 10 Equity:Unknown end test test --input-date-format "%Y-%m-%d" --auto-match convert test/baseline/opt-auto-match.dat 2012/03/01 * Food Expenses:Food 10 Equity:Unknown 2012/03/02 * Phone Expenses:Phone 10 Equity:Unknown 2012/03/02 * Dining Expenses:Food 10 Equity:Unknown end test test --input-date-format "%Y-%m-%d" --account Assets:Bank convert test/baseline/opt-auto-match.dat 2012/03/01 * Food Expenses:Unknown 10 Assets:Bank 2012/03/02 * Phone Expenses:Unknown 10 Assets:Bank 2012/03/02 * Dining Expenses:Unknown 10 Assets:Bank end test test --input-date-format "%Y-%m-%d" --auto-match --account Assets:Bank convert test/baseline/opt-auto-match.dat 2012/03/01 * Food Expenses:Food 10 Assets:Bank 2012/03/02 * Phone Expenses:Phone 10 Assets:Bank 2012/03/02 * Dining Expenses:Food 10 Assets:Bank end test ��������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-aux-date.test��������������������������������������������������������0000664�0000000�0000000�00000001374�14411236400�0020733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31=2008/01/01 End of January Expenses:Books $10.00 ; [=2008/02/01] Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash test reg --aux-date 08-Jan-01 January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 08-Feb-01 End of January Expenses:Books $10.00 $10.00 08-Jan-01 End of January Assets:Cash $-10.00 0 08-Feb-01 February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-average-lot-prices.test����������������������������������������������0000664�0000000�0000000�00000000750�14411236400�0022711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2019-01-01 * Opening balance Assets:Investments 1000.00 EUR Equity:Opening balance -1000.00 EUR 2019-03-01 * Bought FOO Assets:Investments 2 FOO @ 150.00 EUR Assets:Investments -300.00 EUR 2019-06-01 * Bought FOO Assets:Investments 1 FOO @ 180.00 EUR Assets:Investments -180.00 EUR test bal assets:investments --average-lot-prices 520.00 EUR 3 FOO {160.00 EUR} Assets:Investments end test ������������������������ledger-3.3.2/test/baseline/opt-average.test���������������������������������������������������������0000664�0000000�0000000�00000016442�14411236400�0020637�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --average books 08-Jan-01 January Expenses:Books $10.00 $10.00 08-Jan-31 End of January Expenses:Books $10.00 $10.00 08-Feb-01 February Expenses:Books $20.00 $13.33 08-Feb-28 End of February Expenses:Books $20.00 $15.00 08-Mar-01 March Expenses:Books $30.00 $18.00 08-Mar-31 End of March Expenses:Books $30.00 $20.00 08-Apr-01 April Expenses:Books $40.00 $22.86 08-Apr-30 End of April Expenses:Books $40.00 $25.00 08-May-01 May Expenses:Books $50.00 $27.78 08-May-31 End of May Expenses:Books $50.00 $30.00 08-Jun-01 June Expenses:Books $60.00 $32.73 08-Jun-30 End of June Expenses:Books $60.00 $35.00 08-Jul-01 July Expenses:Books $70.00 $37.69 08-Jul-31 End of July Expenses:Books $70.00 $40.00 08-Aug-01 August Expenses:Books $80.00 $42.67 08-Aug-31 End of August Expenses:Books $80.00 $45.00 08-Sep-01 September Expenses:Books $90.00 $47.65 08-Sep-30 End of September Expenses:Books $90.00 $50.00 08-Oct-01 October Expenses:Books $100.00 $52.63 08-Oct-31 End of October Expenses:Books $100.00 $55.00 08-Nov-01 November Expenses:Books $110.00 $57.62 08-Nov-30 End of November Expenses:Books $110.00 $60.00 08-Dec-01 December Expenses:Books $120.00 $62.61 08-Dec-31 End of December Expenses:Books $120.00 $65.00 09-Jan-01 January Expenses:Books $10.00 $62.80 09-Jan-31 End of January Expenses:Books $10.00 $60.77 09-Feb-01 February Expenses:Books $20.00 $59.26 09-Feb-28 End of February Expenses:Books $20.00 $57.86 09-Mar-01 March Expenses:Books $30.00 $56.90 09-Mar-31 End of March Expenses:Books $30.00 $56.00 09-Apr-01 April Expenses:Books $40.00 $55.48 09-Apr-30 End of April Expenses:Books $40.00 $55.00 09-May-01 May Expenses:Books $50.00 $54.85 09-May-31 End of May Expenses:Books $50.00 $54.71 09-Jun-01 June Expenses:Books $60.00 $54.86 09-Jun-30 End of June Expenses:Books $60.00 $55.00 09-Jul-01 July Expenses:Books $70.00 $55.41 09-Jul-31 End of July Expenses:Books $70.00 $55.79 09-Aug-01 August Expenses:Books $80.00 $56.41 09-Aug-31 End of August Expenses:Books $80.00 $57.00 09-Sep-01 September Expenses:Books $90.00 $57.80 09-Sep-30 End of September Expenses:Books $90.00 $58.57 09-Oct-01 October Expenses:Books $100.00 $59.53 09-Oct-31 End of October Expenses:Books $100.00 $60.45 09-Nov-01 November Expenses:Books $110.00 $61.56 09-Nov-30 End of November Expenses:Books $110.00 $62.61 09-Dec-01 December Expenses:Books $120.00 $63.83 09-Dec-31 End of December Expenses:Books $120.00 $65.00 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-balance-format.test��������������������������������������������������0000664�0000000�0000000�00000000401�14411236400�0022064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --balance-format='%(account)\n' --no-total Assets:Investments:Vanguard:VMMXX Income:Dividends:Vanguard:VMMXX end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-base.test������������������������������������������������������������0000664�0000000�0000000�00000001226�14411236400�0020131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������i 2007/03/01 23:00:00 A o 2007/03/02 01:00:00 i 2007/03/11 23:00:00 B o 2007/03/12 01:00:00 2006/05/22 * Company Assets:Receivable $4,000.00 Income:Contracts -40h @ $100.00 2006/05/22 * Company Assets:Receivable $4,000.00 Income:Contracts -40h {$20} @ $100.00 Income:Gains $-3,200.00 test bal --base 7200s A $8,000.00 Assets:Receivable 7200s B $-3,200.00 -288000s Income -288000s Contracts $-3,200.00 Gains -------------------- $4,800.00 -273600s end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-basis.test�����������������������������������������������������������0000664�0000000�0000000�00000000464�14411236400�0020323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --basis $0.35 Assets:Investments:Vanguard:VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-begin.test�����������������������������������������������������������0000664�0000000�0000000�00000015736�14411236400�0020316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --begin=2009/02 09-Feb-01 February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 09-Feb-28 End of February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 09-Mar-01 March Expenses:Books $30.00 $30.00 Assets:Cash $-30.00 0 09-Mar-31 End of March Expenses:Books $30.00 $30.00 Assets:Cash $-30.00 0 09-Apr-01 April Expenses:Books $40.00 $40.00 Assets:Cash $-40.00 0 09-Apr-30 End of April Expenses:Books $40.00 $40.00 Assets:Cash $-40.00 0 09-May-01 May Expenses:Books $50.00 $50.00 Assets:Cash $-50.00 0 09-May-31 End of May Expenses:Books $50.00 $50.00 Assets:Cash $-50.00 0 09-Jun-01 June Expenses:Books $60.00 $60.00 Assets:Cash $-60.00 0 09-Jun-30 End of June Expenses:Books $60.00 $60.00 Assets:Cash $-60.00 0 09-Jul-01 July Expenses:Books $70.00 $70.00 Assets:Cash $-70.00 0 09-Jul-31 End of July Expenses:Books $70.00 $70.00 Assets:Cash $-70.00 0 09-Aug-01 August Expenses:Books $80.00 $80.00 Assets:Cash $-80.00 0 09-Aug-31 End of August Expenses:Books $80.00 $80.00 Assets:Cash $-80.00 0 09-Sep-01 September Expenses:Books $90.00 $90.00 Assets:Cash $-90.00 0 09-Sep-30 End of September Expenses:Books $90.00 $90.00 Assets:Cash $-90.00 0 09-Oct-01 October Expenses:Books $100.00 $100.00 Assets:Cash $-100.00 0 09-Oct-31 End of October Expenses:Books $100.00 $100.00 Assets:Cash $-100.00 0 09-Nov-01 November Expenses:Books $110.00 $110.00 Assets:Cash $-110.00 0 09-Nov-30 End of November Expenses:Books $110.00 $110.00 Assets:Cash $-110.00 0 09-Dec-01 December Expenses:Books $120.00 $120.00 Assets:Cash $-120.00 0 09-Dec-31 End of December Expenses:Books $120.00 $120.00 Assets:Cash $-120.00 0 end test ����������������������������������ledger-3.3.2/test/baseline/opt-bold-if.test���������������������������������������������������������0000664�0000000�0000000�00000001214�14411236400�0020530�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Opening balance Assets:Cash 100.00 EUR Equity:Opening Balance 2012-01-02 * Test ; :test: Expenses:Food 100.00 EUR Assets:Cash -100.00 EUR test reg --bold-if 'has_tag("test")' 12-Jan-01 Opening balance Assets:Cash 100.00 EUR 100.00 EUR Equity:Opening Balance -100.00 EUR 0 12-Jan-02 Test  Expenses:Food   100.00 EUR  100.00 EUR   Assets:Cash   -100.00 EUR  0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-budget-format.test���������������������������������������������������0000664�0000000�0000000�00000001011�14411236400�0021747�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly Expenses:Phone 10.00 GBP Expenses:Rent 550.00 GBP Assets 2012-02-28 * Phone expense Expenses:Phone 20.00 GBP Assets:Cash -20.00 GBP 2012-02-29 * Rent expense Expenses:Rent 530.00 GBP Assets:Cash -530.00 GBP test budget --now 2012-02-29 --budget-format "%(justify(scrub(display_total), 0))\n" (-550.00 GBP, 560.00 GBP) (550.00 GBP, -560.00 GBP) (20.00 GBP, -10.00 GBP) (530.00 GBP, -550.00 GBP) (0, 0) end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-budget.test����������������������������������������������������������0000664�0000000�0000000�00000025673�14411236400�0020505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ monthly Expenses:Books $10.00 Liabilities $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash test reg --budget books --now=2009/12/31 08-Jan-01 Budget transaction Expenses:Books $-10.00 $-10.00 08-Jan-01 January Expenses:Books $10.00 0 08-Jan-31 End of January Expenses:Books $10.00 $10.00 08-Feb-01 Budget transaction Expenses:Books $-10.00 0 08-Feb-01 February Expenses:Books $20.00 $20.00 08-Feb-28 End of February Expenses:Books $20.00 $40.00 08-Mar-01 Budget transaction Expenses:Books $-10.00 $30.00 08-Mar-01 March Expenses:Books $30.00 $60.00 08-Mar-31 End of March Expenses:Books $30.00 $90.00 08-Apr-01 Budget transaction Expenses:Books $-10.00 $80.00 08-Apr-01 April Expenses:Books $40.00 $120.00 08-Apr-30 End of April Expenses:Books $40.00 $160.00 08-May-01 Budget transaction Expenses:Books $-10.00 $150.00 08-May-01 May Expenses:Books $50.00 $200.00 08-May-31 End of May Expenses:Books $50.00 $250.00 08-Jun-01 Budget transaction Expenses:Books $-10.00 $240.00 08-Jun-01 June Expenses:Books $60.00 $300.00 08-Jun-30 End of June Expenses:Books $60.00 $360.00 08-Jul-01 Budget transaction Expenses:Books $-10.00 $350.00 08-Jul-01 July Expenses:Books $70.00 $420.00 08-Jul-31 End of July Expenses:Books $70.00 $490.00 08-Aug-01 Budget transaction Expenses:Books $-10.00 $480.00 08-Aug-01 August Expenses:Books $80.00 $560.00 08-Aug-31 End of August Expenses:Books $80.00 $640.00 08-Sep-01 Budget transaction Expenses:Books $-10.00 $630.00 08-Sep-01 September Expenses:Books $90.00 $720.00 08-Sep-30 End of September Expenses:Books $90.00 $810.00 08-Oct-01 Budget transaction Expenses:Books $-10.00 $800.00 08-Oct-01 October Expenses:Books $100.00 $900.00 08-Oct-31 End of October Expenses:Books $100.00 $1000.00 08-Nov-01 Budget transaction Expenses:Books $-10.00 $990.00 08-Nov-01 November Expenses:Books $110.00 $1100.00 08-Nov-30 End of November Expenses:Books $110.00 $1210.00 08-Dec-01 Budget transaction Expenses:Books $-10.00 $1200.00 08-Dec-01 December Expenses:Books $120.00 $1320.00 08-Dec-31 End of December Expenses:Books $120.00 $1440.00 09-Jan-01 Budget transaction Expenses:Books $-10.00 $1430.00 09-Jan-01 January Expenses:Books $10.00 $1440.00 09-Jan-31 End of January Expenses:Books $10.00 $1450.00 09-Feb-01 Budget transaction Expenses:Books $-10.00 $1440.00 09-Feb-01 February Expenses:Books $20.00 $1460.00 09-Feb-28 End of February Expenses:Books $20.00 $1480.00 09-Mar-01 Budget transaction Expenses:Books $-10.00 $1470.00 09-Mar-01 March Expenses:Books $30.00 $1500.00 09-Mar-31 End of March Expenses:Books $30.00 $1530.00 09-Apr-01 Budget transaction Expenses:Books $-10.00 $1520.00 09-Apr-01 April Expenses:Books $40.00 $1560.00 09-Apr-30 End of April Expenses:Books $40.00 $1600.00 09-May-01 Budget transaction Expenses:Books $-10.00 $1590.00 09-May-01 May Expenses:Books $50.00 $1640.00 09-May-31 End of May Expenses:Books $50.00 $1690.00 09-Jun-01 Budget transaction Expenses:Books $-10.00 $1680.00 09-Jun-01 June Expenses:Books $60.00 $1740.00 09-Jun-30 End of June Expenses:Books $60.00 $1800.00 09-Jul-01 Budget transaction Expenses:Books $-10.00 $1790.00 09-Jul-01 July Expenses:Books $70.00 $1860.00 09-Jul-31 End of July Expenses:Books $70.00 $1930.00 09-Aug-01 Budget transaction Expenses:Books $-10.00 $1920.00 09-Aug-01 August Expenses:Books $80.00 $2000.00 09-Aug-31 End of August Expenses:Books $80.00 $2080.00 09-Sep-01 Budget transaction Expenses:Books $-10.00 $2070.00 09-Sep-01 September Expenses:Books $90.00 $2160.00 09-Sep-30 End of September Expenses:Books $90.00 $2250.00 09-Oct-01 Budget transaction Expenses:Books $-10.00 $2240.00 09-Oct-01 October Expenses:Books $100.00 $2340.00 09-Oct-31 End of October Expenses:Books $100.00 $2440.00 09-Nov-01 Budget transaction Expenses:Books $-10.00 $2430.00 09-Nov-01 November Expenses:Books $110.00 $2540.00 09-Nov-30 End of November Expenses:Books $110.00 $2650.00 09-Dec-01 Budget transaction Expenses:Books $-10.00 $2640.00 09-Dec-01 December Expenses:Books $120.00 $2760.00 09-Dec-31 End of December Expenses:Books $120.00 $2880.00 end test ���������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-budget_only.test�����������������������������������������������������0000664�0000000�0000000�00000001636�14411236400�0021537�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly since 2010/01/01 Expenses:Bills:Rent $873.00 Expenses:Household $200.00 Income:Salary -$2491.60 Assets:Bank:Checking ~ biweekly from 2010/02/23 Expenses:Bills:Housecleaning $85.00 Assets:Bank:Checking 2010/06/22 c897683b Expenses:Household $100.00 Assets:Bank:Checking test reg income --budget --now=2010/06/20 10-Jan-01 Budget transaction Income:Salary $2491.60 $2491.60 10-Feb-01 Budget transaction Income:Salary $2491.60 $4983.20 10-Mar-01 Budget transaction Income:Salary $2491.60 $7474.80 10-Apr-01 Budget transaction Income:Salary $2491.60 $9966.40 10-May-01 Budget transaction Income:Salary $2491.60 $12458.00 10-Jun-01 Budget transaction Income:Salary $2491.60 $14949.60 end test ��������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-budget_range.test����������������������������������������������������0000664�0000000�0000000�00000016232�14411236400�0021650�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ monthly Expenses:Food $100 Expenses:Movies $20 Assets:Cash ~ monthly from 2009 Expenses:Food $101 Expenses:Movies $21 Assets:Cash ~ monthly to 2010 Expenses:Food $102 Expenses:Movies $22 Assets:Cash ~ monthly from 2009 to 2010 Expenses:Food $103 Expenses:Movies $23 Assets:Cash 2009/06/05 Grocery Expenses:Food $5 Assets:Cash test reg --now=2010/02 --sort=date exp --budget 09-Jan-01 Budget transaction Expenses:Food $-101 $-101 09-Jan-01 Budget transaction Expenses:Movies $-21 $-122 09-Jan-01 Budget transaction Expenses:Food $-103 $-225 09-Jan-01 Budget transaction Expenses:Movies $-23 $-248 09-Feb-01 Budget transaction Expenses:Food $-101 $-349 09-Feb-01 Budget transaction Expenses:Movies $-21 $-370 09-Feb-01 Budget transaction Expenses:Food $-103 $-473 09-Feb-01 Budget transaction Expenses:Movies $-23 $-496 09-Mar-01 Budget transaction Expenses:Food $-101 $-597 09-Mar-01 Budget transaction Expenses:Movies $-21 $-618 09-Mar-01 Budget transaction Expenses:Food $-103 $-721 09-Mar-01 Budget transaction Expenses:Movies $-23 $-744 09-Apr-01 Budget transaction Expenses:Food $-101 $-845 09-Apr-01 Budget transaction Expenses:Movies $-21 $-866 09-Apr-01 Budget transaction Expenses:Food $-103 $-969 09-Apr-01 Budget transaction Expenses:Movies $-23 $-992 09-May-01 Budget transaction Expenses:Food $-101 $-1093 09-May-01 Budget transaction Expenses:Movies $-21 $-1114 09-May-01 Budget transaction Expenses:Food $-103 $-1217 09-May-01 Budget transaction Expenses:Movies $-23 $-1240 09-Jun-01 Budget transaction Expenses:Food $-100 $-1340 09-Jun-01 Budget transaction Expenses:Movies $-20 $-1360 09-Jun-01 Budget transaction Expenses:Food $-102 $-1462 09-Jun-01 Budget transaction Expenses:Movies $-22 $-1484 09-Jun-01 Budget transaction Expenses:Food $-101 $-1585 09-Jun-01 Budget transaction Expenses:Movies $-21 $-1606 09-Jun-01 Budget transaction Expenses:Food $-103 $-1709 09-Jun-01 Budget transaction Expenses:Movies $-23 $-1732 09-Jun-05 Grocery Expenses:Food $5 $-1727 09-Jul-01 Budget transaction Expenses:Food $-100 $-1827 09-Jul-01 Budget transaction Expenses:Movies $-20 $-1847 09-Jul-01 Budget transaction Expenses:Food $-101 $-1948 09-Jul-01 Budget transaction Expenses:Movies $-21 $-1969 09-Jul-01 Budget transaction Expenses:Food $-102 $-2071 09-Jul-01 Budget transaction Expenses:Movies $-22 $-2093 09-Jul-01 Budget transaction Expenses:Food $-103 $-2196 09-Jul-01 Budget transaction Expenses:Movies $-23 $-2219 09-Aug-01 Budget transaction Expenses:Food $-100 $-2319 09-Aug-01 Budget transaction Expenses:Movies $-20 $-2339 09-Aug-01 Budget transaction Expenses:Food $-101 $-2440 09-Aug-01 Budget transaction Expenses:Movies $-21 $-2461 09-Aug-01 Budget transaction Expenses:Food $-102 $-2563 09-Aug-01 Budget transaction Expenses:Movies $-22 $-2585 09-Aug-01 Budget transaction Expenses:Food $-103 $-2688 09-Aug-01 Budget transaction Expenses:Movies $-23 $-2711 09-Sep-01 Budget transaction Expenses:Food $-100 $-2811 09-Sep-01 Budget transaction Expenses:Movies $-20 $-2831 09-Sep-01 Budget transaction Expenses:Food $-101 $-2932 09-Sep-01 Budget transaction Expenses:Movies $-21 $-2953 09-Sep-01 Budget transaction Expenses:Food $-102 $-3055 09-Sep-01 Budget transaction Expenses:Movies $-22 $-3077 09-Sep-01 Budget transaction Expenses:Food $-103 $-3180 09-Sep-01 Budget transaction Expenses:Movies $-23 $-3203 09-Oct-01 Budget transaction Expenses:Food $-100 $-3303 09-Oct-01 Budget transaction Expenses:Movies $-20 $-3323 09-Oct-01 Budget transaction Expenses:Food $-101 $-3424 09-Oct-01 Budget transaction Expenses:Movies $-21 $-3445 09-Oct-01 Budget transaction Expenses:Food $-102 $-3547 09-Oct-01 Budget transaction Expenses:Movies $-22 $-3569 09-Oct-01 Budget transaction Expenses:Food $-103 $-3672 09-Oct-01 Budget transaction Expenses:Movies $-23 $-3695 09-Nov-01 Budget transaction Expenses:Food $-100 $-3795 09-Nov-01 Budget transaction Expenses:Movies $-20 $-3815 09-Nov-01 Budget transaction Expenses:Food $-101 $-3916 09-Nov-01 Budget transaction Expenses:Movies $-21 $-3937 09-Nov-01 Budget transaction Expenses:Food $-102 $-4039 09-Nov-01 Budget transaction Expenses:Movies $-22 $-4061 09-Nov-01 Budget transaction Expenses:Food $-103 $-4164 09-Nov-01 Budget transaction Expenses:Movies $-23 $-4187 09-Dec-01 Budget transaction Expenses:Food $-100 $-4287 09-Dec-01 Budget transaction Expenses:Movies $-20 $-4307 09-Dec-01 Budget transaction Expenses:Food $-101 $-4408 09-Dec-01 Budget transaction Expenses:Movies $-21 $-4429 09-Dec-01 Budget transaction Expenses:Food $-102 $-4531 09-Dec-01 Budget transaction Expenses:Movies $-22 $-4553 09-Dec-01 Budget transaction Expenses:Food $-103 $-4656 09-Dec-01 Budget transaction Expenses:Movies $-23 $-4679 10-Jan-01 Budget transaction Expenses:Food $-100 $-4779 10-Jan-01 Budget transaction Expenses:Movies $-20 $-4799 10-Jan-01 Budget transaction Expenses:Food $-101 $-4900 10-Jan-01 Budget transaction Expenses:Movies $-21 $-4921 10-Feb-01 Budget transaction Expenses:Food $-100 $-5021 10-Feb-01 Budget transaction Expenses:Movies $-20 $-5041 10-Feb-01 Budget transaction Expenses:Food $-101 $-5142 10-Feb-01 Budget transaction Expenses:Movies $-21 $-5163 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-by-payee.test��������������������������������������������������������0000664�0000000�0000000�00000012616�14411236400�0020737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --by-payee not @End 08-Apr-01 April Assets:Cash $-80.00 $-80.00 Expenses:Books $80.00 0 08-Aug-01 August Assets:Cash $-160.00 $-160.00 Expenses:Books $160.00 0 08-Dec-01 December Assets:Cash $-240.00 $-240.00 Expenses:Books $240.00 0 08-Feb-01 February Assets:Cash $-40.00 $-40.00 Expenses:Books $40.00 0 08-Jan-01 January Assets:Cash $-20.00 $-20.00 Expenses:Books $20.00 0 08-Jul-01 July Assets:Cash $-140.00 $-140.00 Expenses:Books $140.00 0 08-Jun-01 June Assets:Cash $-120.00 $-120.00 Expenses:Books $120.00 0 08-Mar-01 March Assets:Cash $-60.00 $-60.00 Expenses:Books $60.00 0 08-May-01 May Assets:Cash $-100.00 $-100.00 Expenses:Books $100.00 0 08-Nov-01 November Assets:Cash $-220.00 $-220.00 Expenses:Books $220.00 0 08-Oct-01 October Assets:Cash $-200.00 $-200.00 Expenses:Books $200.00 0 08-Sep-01 September Assets:Cash $-180.00 $-180.00 Expenses:Books $180.00 0 end test ������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-check-payees.test����������������������������������������������������0000664�0000000�0000000�00000001777�14411236400�0021573�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������account Assets:Cash account Expenses:Phone account Expenses:Rent account Expenses:Food commodity EUR commodity GBP payee Phone alias MobilePhone payee Several tag food tag Payee 2012-03-20 Phone Expenses:Phone 20.00 GBP Assets:Cash 2012-03-21 Rent Expenses:Rent 550.00 GBP Assets:Cash 2012-03-22 Food ; :food: Expenses:Food 20.00 EUR Assets:Cash 2012-03-23 Several Expenses:Food 10.00 EUR ; Payee: Food Expenses:Phone 10.00 EUR ; Payee: MobilePhone Assets:Cash test bal --strict --check-payees -40.00 EUR -570.00 GBP Assets:Cash 40.00 EUR 570.00 GBP Expenses 30.00 EUR Food 10.00 EUR 20.00 GBP Phone 550.00 GBP Rent -------------------- 0 __ERROR__ Warning: "$FILE", line 17: Unknown payee 'Rent' Warning: "$FILE", line 21: Unknown payee 'Food' Warning: "$FILE", line 27: Unknown payee 'Food' end test �ledger-3.3.2/test/baseline/opt-cleared-format.test��������������������������������������������������0000664�0000000�0000000�00000002374�14411236400�0022111�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test cleared --file test/input/drewr3.dat --cleared-format "%-30(account) %15(get_at(total_expr, 0)) %15(get_at(total_expr, 1))\n%/" Assets $ -3,804.00 $ 775.00 Assets:Checking $ 1,396.00 $ 775.00 Assets:Checking:Business $ 30.00 0 Assets:Savings $ -5,200.00 0 Equity:Opening Balances $ -1,000.00 $ -1,000.00 Expenses $ 6,654.00 $ 225.00 Expenses:Auto $ 5,500.00 0 Expenses:Books $ 20.00 0 Expenses:Escrow $ 300.00 0 Expenses:Food:Groceries $ 334.00 $ 225.00 Expenses:Interest:Mortgage $ 500.00 0 Income $ -2,030.00 0 Income:Salary $ -2,000.00 0 Income:Sales $ -30.00 0 Liabilities $ -63.60 0 Liabilities:MasterCard $ -20.00 0 Liabilities:Mortgage:Principal $ 200.00 0 Liabilities:Tithe $ -243.60 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-cleared.test���������������������������������������������������������0000664�0000000�0000000�00000010440�14411236400�0020614�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 * February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 * March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April * Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April * Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 * Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 * Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --cleared 08-Jan-01 January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 08-Feb-01 February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 08-Mar-01 March Expenses:Books $30.00 $30.00 Assets:Cash $-30.00 0 08-Apr-01 April Expenses:Books $40.00 $40.00 08-Apr-30 End of April Expenses:Books $40.00 $80.00 08-May-01 May Assets:Cash $-50.00 $30.00 08-May-31 End of May Assets:Cash $-50.00 $-20.00 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-code-as-account.test�������������������������������������������������0000664�0000000�0000000�00000002332�14411236400�0022163�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * (100) January Expenses:Books $10.00 Assets:Cash 2008/01/31 (101) End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 * (102) February Expenses:Books $20.00 Assets:Cash 2008/02/28 (103) End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 * March Expenses:Books $30.00 Assets:Cash test reg --account=code 08-Jan-01 January 100:Expenses:Books $10.00 $10.00 08-Jan-01 January 100:Assets:Cash $-10.00 0 08-Jan-31 End of January 101:Expenses:Books $10.00 $10.00 08-Jan-31 End of January 101:Assets:Cash $-10.00 0 08-Feb-01 February 102:Expenses:Books $20.00 $20.00 08-Feb-01 February 102:Assets:Cash $-20.00 0 08-Feb-28 End of February 103:Expenses:Books $20.00 $20.00 08-Feb-28 End of February 103:Assets:Cash $-20.00 0 08-Mar-01 March Expenses:Books $30.00 $30.00 08-Mar-01 March Assets:Cash $-30.00 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-code-as-payee.test���������������������������������������������������0000664�0000000�0000000�00000002330�14411236400�0021630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * (100) January Expenses:Books $10.00 Assets:Cash 2008/01/31 (101) End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 * (102) February Expenses:Books $20.00 Assets:Cash 2008/02/28 (103) End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 * March Expenses:Books $30.00 Assets:Cash test reg --payee=code 08-Jan-01 100 Expenses:Books $10.00 $10.00 08-Jan-01 100 Assets:Cash $-10.00 0 08-Jan-31 101 Expenses:Books $10.00 $10.00 08-Jan-31 101 Assets:Cash $-10.00 0 08-Feb-01 102 Expenses:Books $20.00 $20.00 08-Feb-01 102 Assets:Cash $-20.00 0 08-Feb-28 103 Expenses:Books $20.00 $20.00 08-Feb-28 103 Assets:Cash $-20.00 0 08-Mar-01 March Expenses:Books $30.00 $30.00 08-Mar-01 March Assets:Cash $-30.00 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-collapse-if-zero.test������������������������������������������������0000664�0000000�0000000�00000001023�14411236400�0022365�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX $0.35 Income:Dividends:Vanguard:VMMXX $-0.35 2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --collapse-if-zero 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-collapse.test��������������������������������������������������������0000664�0000000�0000000�00000000430�14411236400�0021015�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --collapse 0.350 VMMXX Assets $-0.35 Income -------------------- $-0.35 0.350 VMMXX end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-collapse_reg.test����������������������������������������������������0000664�0000000�0000000�00000002353�14411236400�0021660�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/10/01 Test Expenses:Food:Dining $10 Expenses:Food:Tips $1 Assets:Cash 2009/10/02 Test Expenses:Food:Dining $10 Expenses:Food:Tips $1 Assets:Cash 2009/10/03 Test Expenses:Food:Dining $10 Expenses:Food:Tips $1 Assets:Cash 2009/10/04 Test Expenses:Food:Dining $10 Expenses:Food:Tips $1 Assets:Cash 2009/10/05 Test Expenses:Food:Dining $10 Expenses:Food:Tips $1 Assets:Cash 2009/10/06 Test Expenses:Food:Dining $10 Expenses:Food:Tips $1 Assets:Cash 2009/10/07 Test Expenses:Food:Dining $10 Expenses:Food:Tips $1 Assets:Cash test reg --collapse food 09-Oct-01 Test <Total> $11 $11 09-Oct-02 Test <Total> $11 $22 09-Oct-03 Test <Total> $11 $33 09-Oct-04 Test <Total> $11 $44 09-Oct-05 Test <Total> $11 $55 09-Oct-06 Test <Total> $11 $66 09-Oct-07 Test <Total> $11 $77 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-color.test�����������������������������������������������������������0000664�0000000�0000000�00000000511�14411236400�0020331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --color 0.350 VMMXX Assets:Investments:Vanguard:VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- $-0.35 0.350 VMMXX end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-columns.test���������������������������������������������������������0000664�0000000�0000000�00000000716�14411236400�0020702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --columns=100 07-Feb-02 RD VMMXX Asse:Investment:Vanguard:VMMXX 0.350 VMMXX 0.350 VMMXX Incom:Dividends:Vanguard:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ��������������������������������������������������ledger-3.3.2/test/baseline/opt-commodity-as-account.test��������������������������������������������0000664�0000000�0000000�00000000633�14411236400�0023257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --account=commodity 07-Feb-02 RD VMMXX VM:As:Inv:Vangua:VMMXX 0.350 VMMXX 0.350 VMMXX 07-Feb-02 RD VMMXX $:In:Div:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test �����������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-commodity-as-payee.test����������������������������������������������0000664�0000000�0000000�00000000631�14411236400�0022724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --payee=commodity 07-Feb-02 VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX 07-Feb-02 $ In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test �������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-cost.test������������������������������������������������������������0000664�0000000�0000000�00000000463�14411236400�0020171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --cost $0.35 Assets:Investments:Vanguard:VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-count.test�����������������������������������������������������������0000664�0000000�0000000�00000001372�14411236400�0020351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-02-28 Phone expense Expenses:Phone 20.00 GBP Assets:Cash -20.00 GBP 2012-02-29 * Rent expense Expenses:Rent 530.00 GBP Assets:Cash -530.00 GBP 2012-03-03 Phone expense Expenses:Phone 12.00 EUR Assets:Cash -12.00 EUR 2012-03-04 * Bed and breakfast ; Payee: Rent expense ; :bnb: Expenses:Rent 30.00 EUR Assets:Cash -30.00 EUR test accounts --count 4 Assets:Cash 2 Expenses:Phone 2 Expenses:Rent end test test commodities --count 4 EUR 4 GBP end test test payees --count 4 Phone expense 4 Rent expense end test test commodities :rent --count 1 EUR 1 GBP end test test payees tag bnb --count 2 Rent expense end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-csv-format.test������������������������������������������������������0000664�0000000�0000000�00000000311�14411236400�0021272�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test csv --csv-format='"%(date)"\n' "2007/02/02" "2007/02/02" end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-csv-format4180.test��������������������������������������������������0000664�0000000�0000000�00000000300�14411236400�0021605�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2023/01/09 John's Hardware - 2" rubber nails Expenses:DIY $1.00 Assets:Cash test csv --csv-format='%(quoted_rfc(payee))\n' expenses "John's Hardware - 2"" rubber nails" end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-csv-formatq.test�����������������������������������������������������0000664�0000000�0000000�00000000274�14411236400�0021463�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2023/01/09 John's Hardware - 2" rubber nails Expenses:DIY $1.00 Assets:Cash test csv --csv-format='%(quoted(payee))\n' expenses "John's Hardware - 2\" rubber nails" end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-current.test���������������������������������������������������������0000664�0000000�0000000�00000000715�14411236400�0020703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 2700/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --current 0.350 VMMXX Assets:Investments:Vanguard:VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- $-0.35 0.350 VMMXX end test ���������������������������������������������������ledger-3.3.2/test/baseline/opt-daily.test�����������������������������������������������������������0000664�0000000�0000000�00000002342�14411236400�0020321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash test reg --daily 08-Jan-01 - 08-Jan-01 Assets:Cash $-60.00 $-60.00 Expenses:Books $60.00 0 08-Feb-01 - 08-Feb-01 Assets:Cash $-120.00 $-120.00 Expenses:Books $120.00 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-date-format.test�����������������������������������������������������0000664�0000000�0000000�00000000621�14411236400�0021420�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --date-format='%Y' 2007 RD VMMXX As:Investm:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Dividen:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ���������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-date-width.test������������������������������������������������������0000664�0000000�0000000�00000000676�14411236400�0021261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --date-width=20 07-Feb-02 RD VMMXX As:Investm:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Dividen:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-date.test������������������������������������������������������������0000664�0000000�0000000�00000005256�14411236400�0020143�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 GBP 2011-03-04 Buy shares ; date: 2011-04-05 Assets:Broker 2 AAA @ 0.90 GBP Assets:Bank 2011-03-05 Buy shares ; date: 2011-04-06 Assets:Broker 2 AAA @ 1.00 GBP Assets:Bank test reg --input-date-format %Y-%m-%d --date-format %d-%m-%Y --date 'has_tag("date") ? to_date(tag("date")) : date' 05-04-2011 Buy shares Assets:Broker 2 AAA 2 AAA 05-04-2011 Buy shares Assets:Bank -1.80 GBP 2 AAA -1.80 GBP 06-04-2011 Buy shares Assets:Broker 2 AAA 4 AAA -1.80 GBP 06-04-2011 Buy shares Assets:Bank -2.00 GBP 4 AAA -3.80 GBP end test test reg --date 'date + 2' 11-Mar-06 Buy shares Assets:Broker 2 AAA 2 AAA 11-Mar-06 Buy shares Assets:Bank -1.80 GBP 2 AAA -1.80 GBP 11-Mar-07 Buy shares Assets:Broker 2 AAA 4 AAA -1.80 GBP 11-Mar-07 Buy shares Assets:Bank -2.00 GBP 4 AAA -3.80 GBP end test test reg --date 'date - 2' 11-Mar-02 Buy shares Assets:Broker 2 AAA 2 AAA 11-Mar-02 Buy shares Assets:Bank -1.80 GBP 2 AAA -1.80 GBP 11-Mar-03 Buy shares Assets:Broker 2 AAA 4 AAA -1.80 GBP 11-Mar-03 Buy shares Assets:Bank -2.00 GBP 4 AAA -3.80 GBP end test test reg --date 'date + 365*2' 13-Mar-03 Buy shares Assets:Broker 2 AAA 2 AAA 13-Mar-03 Buy shares Assets:Bank -1.80 GBP 2 AAA -1.80 GBP 13-Mar-04 Buy shares Assets:Broker 2 AAA 4 AAA -1.80 GBP 13-Mar-04 Buy shares Assets:Bank -2.00 GBP 4 AAA -3.80 GBP end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-datetime-format.test�������������������������������������������������0000664�0000000�0000000�00000001607�14411236400�0022304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������i 2013/04/05 09:30:00 Internal:Meeting:Tactical Intelligent comment o 2013/04/05 10:00:00 i 2013/04/05 10:00:00 CustomerA:Email o 2013/04/05 10:05:00 i 2013/04/05 10:05:00 CustomerB:Config o 2013/04/05 11:30:00 i 2013/04/05 11:30:00 Personal:Walk o 2013/04/05 12:00:00 i 2013/04/05 12:00:00 Personal:Lunch o 2013/04/05 13:30:00 test bal --time-report --datetime-format '%m/%d/%y %I:%M %p' 04/05/13 10:00 AM 04/05/13 10:05 AM 5.0m CustomerA:Email 04/05/13 10:05 AM 04/05/13 11:30 AM 1.42h CustomerB:Config 04/05/13 09:30 AM 04/05/13 10:00 AM 30.0m Internal:Meeting:Tactical 2.00h Personal 04/05/13 12:00 PM 04/05/13 01:30 PM 1.50h Lunch 04/05/13 11:30 AM 04/05/13 12:00 PM 30.0m Walk -------------------------------------------------- end test �������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-day-break.test�������������������������������������������������������0000664�0000000�0000000�00000000664�14411236400�0021063�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������i 05/10/2011 08:58:37 682 o 05/12/2011 11:25:21 test reg --base 11-May-10 (682) 181604s 181604s end test test reg --base --day-break 11-May-10 (682) 54083s 54083s 11-May-11 (682) 86400s 140483s 11-May-12 (682) 41121s 181604s end test ����������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-dc.test��������������������������������������������������������������0000664�0000000�0000000�00000002563�14411236400�0017612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-10 Employer Assets:Cash $100 Income:Employer 2012-03-10 KFC Expenses:Food $20 Assets:Cash 2012-03-10 KFC - Rebate Assets:Cash Expenses:Food $-5 2012-03-10 KFC - Food & Rebate Expenses:Food $20 Expenses:Food $-5 Assets:Cash test reg --dc 12-Mar-10 Employer Assets:Cash $100 0 $100 Incom:Employer 0 $100 0 12-Mar-10 KFC Expenses:Food $20 0 $20 Assets:Cash 0 $20 0 12-Mar-10 KFC - Rebate Assets:Cash $5 0 $5 Expenses:Food 0 $5 0 12-Mar-10 KFC - Food & R.. Expenses:Food $20 0 $20 Expenses:Food 0 $5 $15 Assets:Cash 0 $15 0 end test test bal --dc $105 $35 $70 Assets:Cash $40 $10 $30 Expenses:Food 0 $100 $-100 Income:Employer -------------------------------------------- $145 $145 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-decimal-comma.test���������������������������������������������������0000664�0000000�0000000�00000001031�14411236400�0021701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Opening balance Assets:Cash 100,00 EUR Equity:Opening Balance 2012-01-02 * Test Expenses:Food 10,00 EUR Assets:Cash -10,00 EUR 2012-01-03 * Test Expenses:Food €10,00 Assets:Cash €-10,00 test --decimal-comma bal 90,00 EUR €-10,00 Assets:Cash -100,00 EUR Equity:Opening Balance 10,00 EUR €10,00 Expenses:Food -------------------- 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-depth.test�����������������������������������������������������������0000664�0000000�0000000�00000004163�14411236400�0020326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books $10.00 Assets:Cash 2008/01/01 January Assets:Cash 0 Income:Books 2008/01/01 January Assets:Cash Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books $-10.00 test bal --depth 1 $-10.00 Assets $40.00 Expenses $-30.00 Income -------------------- 0 end test test bal --depth 2 $-10.00 Assets:Cash $40.00 Expenses $10.00 Books $30.00 One $-30.00 Income:One -------------------- 0 end test test bal --depth 3 $-10.00 Assets:Cash $40.00 Expenses $10.00 Books $30.00 One $10.00 Books $20.00 Two $-30.00 Income:One $-10.00 Books $-20.00 Two -------------------- 0 end test test bal --depth 4 $-10.00 Assets:Cash $40.00 Expenses $10.00 Books $30.00 One $10.00 Books $20.00 Two $10.00 Books $10.00 Three $-30.00 Income:One $-10.00 Books $-20.00 Two $-10.00 Books $-10.00 Three -------------------- 0 end test test bal --depth 5 $-10.00 Assets:Cash $40.00 Expenses $10.00 Books $30.00 One $10.00 Books $20.00 Two $10.00 Books $10.00 Three:Books $-30.00 Income:One $-10.00 Books $-20.00 Two $-10.00 Books $-10.00 Three:Books -------------------- 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-depth_flat.test������������������������������������������������������0000664�0000000�0000000�00000003107�14411236400�0021331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books $10.00 Assets:Cash 2008/01/01 January Assets:Cash 0 Income:Books 2008/01/01 January Assets:Cash Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books $-10.00 test bal --depth 1 --flat end test test bal --depth 2 --flat $-10.00 Assets:Cash $10.00 Expenses:Books -------------------- 0 end test test bal --depth 3 --flat $-10.00 Assets:Cash $10.00 Expenses:Books $10.00 Expenses:One:Books $-10.00 Income:One:Books -------------------- 0 end test test bal --depth 4 --flat $-10.00 Assets:Cash $10.00 Expenses:Books $10.00 Expenses:One:Books $10.00 Expenses:One:Two:Books $-10.00 Income:One:Books $-10.00 Income:One:Two:Books -------------------- 0 end test test bal --depth 5 --flat $-10.00 Assets:Cash $10.00 Expenses:Books $10.00 Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books $-10.00 Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books -------------------- 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-deviation.test�������������������������������������������������������0000664�0000000�0000000�00000016447�14411236400�0021214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg -A --deviation books 08-Jan-01 January Expenses:Books $10.00 0 08-Jan-31 End of January Expenses:Books $10.00 0 08-Feb-01 February Expenses:Books $20.00 $6.67 08-Feb-28 End of February Expenses:Books $20.00 $5.00 08-Mar-01 March Expenses:Books $30.00 $12.00 08-Mar-31 End of March Expenses:Books $30.00 $10.00 08-Apr-01 April Expenses:Books $40.00 $17.14 08-Apr-30 End of April Expenses:Books $40.00 $15.00 08-May-01 May Expenses:Books $50.00 $22.22 08-May-31 End of May Expenses:Books $50.00 $20.00 08-Jun-01 June Expenses:Books $60.00 $27.27 08-Jun-30 End of June Expenses:Books $60.00 $25.00 08-Jul-01 July Expenses:Books $70.00 $32.31 08-Jul-31 End of July Expenses:Books $70.00 $30.00 08-Aug-01 August Expenses:Books $80.00 $37.33 08-Aug-31 End of August Expenses:Books $80.00 $35.00 08-Sep-01 September Expenses:Books $90.00 $42.35 08-Sep-30 End of September Expenses:Books $90.00 $40.00 08-Oct-01 October Expenses:Books $100.00 $47.37 08-Oct-31 End of October Expenses:Books $100.00 $45.00 08-Nov-01 November Expenses:Books $110.00 $52.38 08-Nov-30 End of November Expenses:Books $110.00 $50.00 08-Dec-01 December Expenses:Books $120.00 $57.39 08-Dec-31 End of December Expenses:Books $120.00 $55.00 09-Jan-01 January Expenses:Books $10.00 $-52.80 09-Jan-31 End of January Expenses:Books $10.00 $-50.77 09-Feb-01 February Expenses:Books $20.00 $-39.26 09-Feb-28 End of February Expenses:Books $20.00 $-37.86 09-Mar-01 March Expenses:Books $30.00 $-26.90 09-Mar-31 End of March Expenses:Books $30.00 $-26.00 09-Apr-01 April Expenses:Books $40.00 $-15.48 09-Apr-30 End of April Expenses:Books $40.00 $-15.00 09-May-01 May Expenses:Books $50.00 $-4.85 09-May-31 End of May Expenses:Books $50.00 $-4.71 09-Jun-01 June Expenses:Books $60.00 $5.14 09-Jun-30 End of June Expenses:Books $60.00 $5.00 09-Jul-01 July Expenses:Books $70.00 $14.59 09-Jul-31 End of July Expenses:Books $70.00 $14.21 09-Aug-01 August Expenses:Books $80.00 $23.59 09-Aug-31 End of August Expenses:Books $80.00 $23.00 09-Sep-01 September Expenses:Books $90.00 $32.20 09-Sep-30 End of September Expenses:Books $90.00 $31.43 09-Oct-01 October Expenses:Books $100.00 $40.47 09-Oct-31 End of October Expenses:Books $100.00 $39.55 09-Nov-01 November Expenses:Books $110.00 $48.44 09-Nov-30 End of November Expenses:Books $110.00 $47.39 09-Dec-01 December Expenses:Books $120.00 $56.17 09-Dec-31 End of December Expenses:Books $120.00 $55.00 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-display-amount.test��������������������������������������������������0000664�0000000�0000000�00000003610�14411236400�0022164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash test reg --display-amount='amount * 10' books 08-Jan-01 January Expenses:Books $100.00 $10.00 08-Jan-01 End of January Expenses:Books $100.00 $20.00 08-Jan-01 January Expenses:Books $100.00 $30.00 08-Jan-01 End of January Expenses:Books $100.00 $40.00 08-Jan-01 January Expenses:Books $100.00 $50.00 08-Jan-01 End of January Expenses:Books $100.00 $60.00 08-Feb-01 February Expenses:Books $200.00 $80.00 08-Feb-01 End of February Expenses:Books $200.00 $100.00 08-Feb-01 February Expenses:Books $200.00 $120.00 08-Feb-01 End of February Expenses:Books $200.00 $140.00 08-Feb-01 February Expenses:Books $200.00 $160.00 08-Feb-01 End of February Expenses:Books $200.00 $180.00 end test ������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-display-total.test���������������������������������������������������0000664�0000000�0000000�00000003607�14411236400�0022012�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash test reg --display-total='amount * 10' books 08-Jan-01 January Expenses:Books $10.00 $100.00 08-Jan-01 End of January Expenses:Books $10.00 $100.00 08-Jan-01 January Expenses:Books $10.00 $100.00 08-Jan-01 End of January Expenses:Books $10.00 $100.00 08-Jan-01 January Expenses:Books $10.00 $100.00 08-Jan-01 End of January Expenses:Books $10.00 $100.00 08-Feb-01 February Expenses:Books $20.00 $200.00 08-Feb-01 End of February Expenses:Books $20.00 $200.00 08-Feb-01 February Expenses:Books $20.00 $200.00 08-Feb-01 End of February Expenses:Books $20.00 $200.00 08-Feb-01 February Expenses:Books $20.00 $200.00 08-Feb-01 End of February Expenses:Books $20.00 $200.00 end test �������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-display.test���������������������������������������������������������0000664�0000000�0000000�00000002643�14411236400�0020670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash test reg --display='payee =~ /February/' books 08-Feb-01 February Expenses:Books $20.00 $80.00 08-Feb-01 End of February Expenses:Books $20.00 $100.00 08-Feb-01 February Expenses:Books $20.00 $120.00 08-Feb-01 End of February Expenses:Books $20.00 $140.00 08-Feb-01 February Expenses:Books $20.00 $160.00 08-Feb-01 End of February Expenses:Books $20.00 $180.00 end test ���������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-dow.test�������������������������������������������������������������0000664�0000000�0000000�00000002105�14411236400�0020005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/01 End of February Expenses:Books $20.00 Assets:Cash test reg --dow books 08-Jan-01 Tuesdays Expenses:Books $60.00 $60.00 08-Feb-01 Fridays Expenses:Books $120.00 $180.00 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-empty.test�����������������������������������������������������������0000664�0000000�0000000�00000002662�14411236400�0020362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books $10.00 Assets:Cash 2008/01/01 January Assets:Cash 0 Income:Books 2008/01/01 January Assets:Cash Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books $-10.00 test reg --empty 08-Jan-01 January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 08-Jan-01 January Expenses:One:Books $10.00 $10.00 Expenses:One:Two:Books $10.00 $20.00 Ex:One:Two:Three:Books $10.00 $30.00 Assets:Cash $-30.00 0 08-Jan-01 January Assets:Cash 0 0 Income:Books 0 0 08-Jan-01 January Assets:Cash $30.00 $30.00 Income:One:Books $-10.00 $20.00 Income:One:Two:Books $-10.00 $10.00 In:One:Two:Three:Books $-10.00 0 end test ������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-empty_bal.test�������������������������������������������������������0000664�0000000�0000000�00000001762�14411236400�0021200�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books $10.00 Assets:Cash 2008/01/01 January Assets:Cash 0 Income:Books 2008/01/01 January Assets:Cash Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books $-10.00 test bal --empty $-10.00 Assets:Cash $40.00 Expenses $10.00 Books $30.00 One $10.00 Books $20.00 Two $10.00 Books $10.00 Three:Books $-30.00 Income 0 Books $-30.00 One $-10.00 Books $-20.00 Two $-10.00 Books $-10.00 Three:Books -------------------- 0 end test ��������������ledger-3.3.2/test/baseline/opt-empty_bal_flat.test��������������������������������������������������0000664�0000000�0000000�00000001615�14411236400�0022203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books $10.00 Assets:Cash 2008/01/01 January Assets:Cash 0 Income:Books 2008/01/01 January Assets:Cash Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books $-10.00 test bal --empty --flat $-10.00 Assets:Cash $10.00 Expenses:Books $10.00 Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books 0 Income:Books $-10.00 Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books -------------------- 0 end test �������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-empty_collapse.test��������������������������������������������������0000664�0000000�0000000�00000001465�14411236400�0022244�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books $10.00 Assets:Cash 2008/01/01 January Assets:Cash 0 Income:Books 2008/01/01 January Assets:Cash Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books $-10.00 test reg --empty --collapse 08-Jan-01 January <Total> 0 0 08-Jan-01 January <Total> 0 0 08-Jan-01 January <Total> 0 0 08-Jan-01 January <Total> 0 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-end.test�������������������������������������������������������������0000664�0000000�0000000�00000017144�14411236400�0017773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --end=2009/02 08-Jan-01 January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 08-Jan-31 End of January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 08-Feb-01 February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 08-Feb-28 End of February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 08-Mar-01 March Expenses:Books $30.00 $30.00 Assets:Cash $-30.00 0 08-Mar-31 End of March Expenses:Books $30.00 $30.00 Assets:Cash $-30.00 0 08-Apr-01 April Expenses:Books $40.00 $40.00 Assets:Cash $-40.00 0 08-Apr-30 End of April Expenses:Books $40.00 $40.00 Assets:Cash $-40.00 0 08-May-01 May Expenses:Books $50.00 $50.00 Assets:Cash $-50.00 0 08-May-31 End of May Expenses:Books $50.00 $50.00 Assets:Cash $-50.00 0 08-Jun-01 June Expenses:Books $60.00 $60.00 Assets:Cash $-60.00 0 08-Jun-30 End of June Expenses:Books $60.00 $60.00 Assets:Cash $-60.00 0 08-Jul-01 July Expenses:Books $70.00 $70.00 Assets:Cash $-70.00 0 08-Jul-31 End of July Expenses:Books $70.00 $70.00 Assets:Cash $-70.00 0 08-Aug-01 August Expenses:Books $80.00 $80.00 Assets:Cash $-80.00 0 08-Aug-31 End of August Expenses:Books $80.00 $80.00 Assets:Cash $-80.00 0 08-Sep-01 September Expenses:Books $90.00 $90.00 Assets:Cash $-90.00 0 08-Sep-30 End of September Expenses:Books $90.00 $90.00 Assets:Cash $-90.00 0 08-Oct-01 October Expenses:Books $100.00 $100.00 Assets:Cash $-100.00 0 08-Oct-31 End of October Expenses:Books $100.00 $100.00 Assets:Cash $-100.00 0 08-Nov-01 November Expenses:Books $110.00 $110.00 Assets:Cash $-110.00 0 08-Nov-30 End of November Expenses:Books $110.00 $110.00 Assets:Cash $-110.00 0 08-Dec-01 December Expenses:Books $120.00 $120.00 Assets:Cash $-120.00 0 08-Dec-31 End of December Expenses:Books $120.00 $120.00 Assets:Cash $-120.00 0 09-Jan-01 January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 09-Jan-31 End of January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-equity.test����������������������������������������������������������0000664�0000000�0000000�00000004623�14411236400�0020543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 GBP 2011-03-04 Buy shares Assets:Broker 2 AAA @ 0.90 GBP Assets:Bank 2011-03-05 Buy shares Assets:Broker 2 AAA @ 1.00 GBP Assets:Bank test reg --equity 11-Mar-05 Opening Balances Assets:Bank -3.80 GBP -3.80 GBP Assets:Broker 4 AAA 4 AAA -3.80 GBP Equit:Opening Balances -4 AAA -3.80 GBP Equit:Opening Balances 3.80 GBP 0 end test test reg assets --equity 11-Mar-05 Opening Balances Assets:Bank -3.80 GBP -3.80 GBP Assets:Broker 4 AAA 4 AAA -3.80 GBP Equit:Opening Balances -4 AAA -3.80 GBP Equit:Opening Balances 3.80 GBP 0 end test test reg assets:bank --equity 11-Mar-05 Opening Balances Assets:Bank -3.80 GBP -3.80 GBP Equit:Opening Balances 3.80 GBP 0 end test test reg assets:broker --equity 11-Mar-05 Opening Balances Assets:Broker 4 AAA 4 AAA Equit:Opening Balances -4 AAA 0 end test test reg --lots --date-format %Y/%m/%d --equity 2011/03/05 Opening Balances Assets:Bank -3.80 GBP -3.80 GBP Assets:Broker 2 AAA {0.90 GBP} [2011/03/04] 2 AAA {0.90 GBP} [2011/03/04] -3.80 GBP Assets:Broker 2 AAA {1.00 GBP} [2011/03/05] 2 AAA {0.90 GBP} [2011/03/04] 2 AAA {1.00 GBP} [2011/03/05] -3.80 GBP Equit:Opening Balances -2 AAA {0.90 GBP} [2011/03/04] 2 AAA {1.00 GBP} [2011/03/05] -3.80 GBP Equit:Opening Balances -2 AAA {1.00 GBP} [2011/03/05] -3.80 GBP Equit:Opening Balances 3.80 GBP 0 end test �������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-exact.test�����������������������������������������������������������0000664�0000000�0000000�00000013446�14411236400�0020332�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --weekly --exact books 08-Jan-01 - 08-Jan-01 Expenses:Books $10.00 $10.00 08-Jan-31 - 08-Feb-01 Expenses:Books $30.00 $40.00 08-Feb-28 - 08-Mar-01 Expenses:Books $50.00 $90.00 08-Mar-31 - 08-Apr-01 Expenses:Books $70.00 $160.00 08-Apr-30 - 08-May-01 Expenses:Books $90.00 $250.00 08-May-31 - 08-May-31 Expenses:Books $50.00 $300.00 08-Jun-01 - 08-Jun-01 Expenses:Books $60.00 $360.00 08-Jun-30 - 08-Jul-01 Expenses:Books $130.00 $490.00 08-Jul-31 - 08-Aug-01 Expenses:Books $150.00 $640.00 08-Aug-31 - 08-Sep-01 Expenses:Books $170.00 $810.00 08-Sep-30 - 08-Oct-01 Expenses:Books $190.00 $1000.00 08-Oct-31 - 08-Nov-01 Expenses:Books $210.00 $1210.00 08-Nov-30 - 08-Dec-01 Expenses:Books $230.00 $1440.00 08-Dec-31 - 09-Jan-01 Expenses:Books $130.00 $1570.00 09-Jan-31 - 09-Jan-31 Expenses:Books $10.00 $1580.00 09-Feb-01 - 09-Feb-01 Expenses:Books $20.00 $1600.00 09-Feb-28 - 09-Feb-28 Expenses:Books $20.00 $1620.00 09-Mar-01 - 09-Mar-01 Expenses:Books $30.00 $1650.00 09-Mar-31 - 09-Apr-01 Expenses:Books $70.00 $1720.00 09-Apr-30 - 09-May-01 Expenses:Books $90.00 $1810.00 09-May-31 - 09-Jun-01 Expenses:Books $110.00 $1920.00 09-Jun-30 - 09-Jul-01 Expenses:Books $130.00 $2050.00 09-Jul-31 - 09-Aug-01 Expenses:Books $150.00 $2200.00 09-Aug-31 - 09-Sep-01 Expenses:Books $170.00 $2370.00 09-Sep-30 - 09-Oct-01 Expenses:Books $190.00 $2560.00 09-Oct-31 - 09-Oct-31 Expenses:Books $100.00 $2660.00 09-Nov-01 - 09-Nov-01 Expenses:Books $110.00 $2770.00 09-Nov-30 - 09-Dec-01 Expenses:Books $230.00 $3000.00 09-Dec-31 - 09-Dec-31 Expenses:Books $120.00 $3120.00 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-exchange.test��������������������������������������������������������0000664�0000000�0000000�00000017614�14411236400�0021011�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/01/01 January 1st, 2009 (1) Assets:Brokerage 100 A Assets:Brokerage -200 B 2009/01/01 January 1st, 2009 (2) Assets:Brokerage 100 A Assets:Brokerage -300 B 2009/01/01 January 1st, 2009 (3) Assets:Brokerage 100 A Assets:Brokerage -400 B 2009/01/02 January 2nd, 2009 Assets:Brokerage 250 B Assets:Brokerage -500 C 2009/01/03 January 3rd, 2009 Assets:Brokerage 600 C Assets:Brokerage -1000 D 2009/01/04 January 4th, 2009 Assets:Brokerage 300 A Assets:Brokerage -15000 F 2009/01/05 January 5th, 2009 Assets:Brokerage 2000 E Assets:Brokerage -8000 F 2009/01/06 January 6th, 2009 Assets:Brokerage 155 A @ 2 D Assets:Brokerage 2009/01/07 January 7th, 2009 Assets:Brokerage 155 A @@ 200 C Assets:Brokerage 2009/01/08 January 8th, 2009 Assets:Brokerage 155 A (A123) @@ 500 F Assets:Brokerage 2009/01/09 January 9th, 2009 Assets:Brokerage 1000.00 E Assets:Brokerage -155 A {2 D} 2009/01/10 January 10th, 2009 Assets:Brokerage $2,000.00 Assets:Brokerage -155 A [2009/01/06] test reg --exchange=' C, A ' 09-Jan-01 January 1st, 2009 (1) Assets:Brokerage 100 A 100 A Assets:Brokerage -50 A 50 A 09-Jan-01 January 1st, 2009 (2) Assets:Brokerage 100 A 150 A Assets:Brokerage -75 A 75 A 09-Jan-01 January 1st, 2009 (3) Assets:Brokerage 100 A 175 A Assets:Brokerage -100 A 75 A 09-Jan-02 Commodities revalued <Revalued> 225 A -1800 C 300 A -1800 C 09-Jan-02 January 2nd, 2009 Assets:Brokerage 500 C 300 A -1300 C Assets:Brokerage -500 C 300 A -1800 C 09-Jan-03 January 3rd, 2009 Assets:Brokerage 600 C 300 A -1200 C Assets:Brokerage -600 C 300 A -1800 C 09-Jan-04 January 4th, 2009 Assets:Brokerage 300 A 600 A -1800 C Assets:Brokerage -2400 C 600 A -4200 C 09-Jan-05 January 5th, 2009 Assets:Brokerage 1280 C 600 A -2920 C Assets:Brokerage -1280 C 600 A -4200 C 09-Jan-06 Commodities revalued <Revalued> 2040 C 600 A -2160 C 09-Jan-06 January 6th, 2009 Assets:Brokerage 155 A 755 A -2160 C Assets:Brokerage -186 C 755 A -2346 C 09-Jan-07 Commodities revalued <Revalued> -86 C 755 A -2432 C 09-Jan-07 January 7th, 2009 Assets:Brokerage 155 A 910 A -2432 C Assets:Brokerage -200 C 910 A -2632 C 09-Jan-08 Commodities revalued <Revalued> -5613 C 910 A -8245 C 09-Jan-08 January 8th, 2009 Assets:Brokerage 155 A 1065 A -8245 C Assets:Brokerage -200 C 1065 A -8445 C 09-Jan-09 Commodities revalued <Revalued> -2800 C 1065 A -11245 C 09-Jan-09 January 9th, 2009 Assets:Brokerage 200 C 1065 A -11045 C Assets:Brokerage -155 A 910 A -11045 C 09-Jan-10 January 10th, 2009 Assets:Brokerage 200 C 910 A -10845 C Assets:Brokerage -155 A 755 A -10845 C end test test reg --exchange=' C!, A ' 09-Jan-01 January 1st, 2009 (1) Assets:Brokerage 100 A 100 A Assets:Brokerage -50 A 50 A 09-Jan-01 January 1st, 2009 (2) Assets:Brokerage 100 A 150 A Assets:Brokerage -75 A 75 A 09-Jan-01 January 1st, 2009 (3) Assets:Brokerage 100 A 175 A Assets:Brokerage -100 A 75 A 09-Jan-02 Commodities revalued <Revalued> 0 600 C 09-Jan-02 January 2nd, 2009 Assets:Brokerage 500 C 1100 C Assets:Brokerage -500 C 600 C 09-Jan-03 January 3rd, 2009 Assets:Brokerage 600 C 1200 C Assets:Brokerage -600 C 600 C 09-Jan-04 January 4th, 2009 Assets:Brokerage 2400 C 3000 C Assets:Brokerage -2400 C 600 C 09-Jan-05 January 5th, 2009 Assets:Brokerage 1280 C 1880 C Assets:Brokerage -1280 C 600 C 09-Jan-06 Commodities revalued <Revalued> -2040 C -1440 C 09-Jan-06 January 6th, 2009 Assets:Brokerage 186 C -1254 C Assets:Brokerage -186 C -1440 C 09-Jan-07 Commodities revalued <Revalued> -18 C -1458 C 09-Jan-07 January 7th, 2009 Assets:Brokerage 200 C -1258 C Assets:Brokerage -200 C -1458 C 09-Jan-08 Commodities revalued <Revalued> -5613 C -7071 C 09-Jan-08 January 8th, 2009 Assets:Brokerage 200 C -6871 C Assets:Brokerage -200 C -7071 C 09-Jan-09 Commodities revalued <Revalued> -2800 C -9871 C 09-Jan-09 January 9th, 2009 Assets:Brokerage 200 C -9671 C Assets:Brokerage -200 C -9871 C 09-Jan-10 January 10th, 2009 Assets:Brokerage 200 C -9671 C Assets:Brokerage -200 C -9871 C end test ��������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-explicit.test��������������������������������������������������������0000664�0000000�0000000�00000001426�14411236400�0021042�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������account Assets:Cash account Expenses:Phone account Expenses:Rent commodity GBP tag bar 2012-03-20 * Phone ; :bar: Expenses:Phone 20.00 GBP Assets:Cash 2012-03-21 * Rent Expenses:Rent 550.00 GBP Assets:Cash 2012-03-22 * Food ; :food: Expenses:Food 20.00 EUR Assets:Cash test bal --explicit --strict -20.00 EUR -570.00 GBP Assets:Cash 20.00 EUR 570.00 GBP Expenses 20.00 EUR Food 20.00 GBP Phone 550.00 GBP Rent -------------------- 0 __ERROR__ Warning: "$FILE", line 18: Unknown account 'Expenses:Food' Warning: "$FILE", line 18: Unknown commodity 'EUR' Warning: "$FILE", line 19: Unknown metadata tag 'food' end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-file.test������������������������������������������������������������0000664�0000000�0000000�00000000466�14411236400�0020143�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test -f opt-file-does-not-exist.dat bal -> 1 __ERROR__ Error: Cannot read journal file "$sourcepath/opt-file-does-not-exist.dat" end test test -f test/baseline/opt-file1.dat -f test/baseline/opt-file2.dat bal 10 A -10 C -------------------- 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-file1.dat������������������������������������������������������������0000664�0000000�0000000�00000000054�14411236400�0020006�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-22 * Test 1 A 10.00 B ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-file2.dat������������������������������������������������������������0000664�0000000�0000000�00000000054�14411236400�0020007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-22 * Test 2 B 10.00 C ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-flat.test������������������������������������������������������������0000664�0000000�0000000�00000001557�14411236400�0020154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books $10.00 Assets:Cash 2008/01/01 January Assets:Cash Income:Books $-10.00 2008/01/01 January Assets:Cash Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books $-10.00 test bal --flat $10.00 Expenses:Books $10.00 Expenses:One:Books $10.00 Expenses:One:Two:Books $10.00 Expenses:One:Two:Three:Books $-10.00 Income:Books $-10.00 Income:One:Books $-10.00 Income:One:Two:Books $-10.00 Income:One:Two:Three:Books -------------------- 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-force-color.test�����������������������������������������������������0000664�0000000�0000000�00000001063�14411236400�0021430�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --force-color 0.350 VMMXX Assets:Investments:Vanguard:VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- $-0.35 0.350 VMMXX end test test bal 0.350 VMMXX Assets:Investments:Vanguard:VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- $-0.35 0.350 VMMXX end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-forecast-while.test��������������������������������������������������0000664�0000000�0000000�00000024475�14411236400�0022146�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ monthly Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --now=2009/03/21 --forecast-while='total < $3500' books 08-Jan-01 January Expenses:Books $10.00 $10.00 08-Jan-31 End of January Expenses:Books $10.00 $20.00 08-Feb-01 February Expenses:Books $20.00 $40.00 08-Feb-28 End of February Expenses:Books $20.00 $60.00 08-Mar-01 March Expenses:Books $30.00 $90.00 08-Mar-31 End of March Expenses:Books $30.00 $120.00 08-Apr-01 April Expenses:Books $40.00 $160.00 08-Apr-30 End of April Expenses:Books $40.00 $200.00 08-May-01 May Expenses:Books $50.00 $250.00 08-May-31 End of May Expenses:Books $50.00 $300.00 08-Jun-01 June Expenses:Books $60.00 $360.00 08-Jun-30 End of June Expenses:Books $60.00 $420.00 08-Jul-01 July Expenses:Books $70.00 $490.00 08-Jul-31 End of July Expenses:Books $70.00 $560.00 08-Aug-01 August Expenses:Books $80.00 $640.00 08-Aug-31 End of August Expenses:Books $80.00 $720.00 08-Sep-01 September Expenses:Books $90.00 $810.00 08-Sep-30 End of September Expenses:Books $90.00 $900.00 08-Oct-01 October Expenses:Books $100.00 $1000.00 08-Oct-31 End of October Expenses:Books $100.00 $1100.00 08-Nov-01 November Expenses:Books $110.00 $1210.00 08-Nov-30 End of November Expenses:Books $110.00 $1320.00 08-Dec-01 December Expenses:Books $120.00 $1440.00 08-Dec-31 End of December Expenses:Books $120.00 $1560.00 09-Jan-01 January Expenses:Books $10.00 $1570.00 09-Jan-31 End of January Expenses:Books $10.00 $1580.00 09-Feb-01 February Expenses:Books $20.00 $1600.00 09-Feb-28 End of February Expenses:Books $20.00 $1620.00 09-Mar-01 March Expenses:Books $30.00 $1650.00 09-Mar-31 End of March Expenses:Books $30.00 $1680.00 09-Apr-01 April Expenses:Books $40.00 $1720.00 09-Apr-30 End of April Expenses:Books $40.00 $1760.00 09-May-01 May Expenses:Books $50.00 $1810.00 09-May-31 End of May Expenses:Books $50.00 $1860.00 09-Jun-01 June Expenses:Books $60.00 $1920.00 09-Jun-30 End of June Expenses:Books $60.00 $1980.00 09-Jul-01 July Expenses:Books $70.00 $2050.00 09-Jul-31 End of July Expenses:Books $70.00 $2120.00 09-Aug-01 August Expenses:Books $80.00 $2200.00 09-Aug-31 End of August Expenses:Books $80.00 $2280.00 09-Sep-01 September Expenses:Books $90.00 $2370.00 09-Sep-30 End of September Expenses:Books $90.00 $2460.00 09-Oct-01 October Expenses:Books $100.00 $2560.00 09-Oct-31 End of October Expenses:Books $100.00 $2660.00 09-Nov-01 November Expenses:Books $110.00 $2770.00 09-Nov-30 End of November Expenses:Books $110.00 $2880.00 09-Dec-01 December Expenses:Books $120.00 $3000.00 09-Dec-31 End of December Expenses:Books $120.00 $3120.00 09-Apr-01 Forecast transaction Expenses:Books $10.00 $3130.00 09-May-01 Forecast transaction Expenses:Books $10.00 $3140.00 09-Jun-01 Forecast transaction Expenses:Books $10.00 $3150.00 09-Jul-01 Forecast transaction Expenses:Books $10.00 $3160.00 09-Aug-01 Forecast transaction Expenses:Books $10.00 $3170.00 09-Sep-01 Forecast transaction Expenses:Books $10.00 $3180.00 09-Oct-01 Forecast transaction Expenses:Books $10.00 $3190.00 09-Nov-01 Forecast transaction Expenses:Books $10.00 $3200.00 09-Dec-01 Forecast transaction Expenses:Books $10.00 $3210.00 10-Jan-01 Forecast transaction Expenses:Books $10.00 $3220.00 10-Feb-01 Forecast transaction Expenses:Books $10.00 $3230.00 10-Mar-01 Forecast transaction Expenses:Books $10.00 $3240.00 10-Apr-01 Forecast transaction Expenses:Books $10.00 $3250.00 10-May-01 Forecast transaction Expenses:Books $10.00 $3260.00 10-Jun-01 Forecast transaction Expenses:Books $10.00 $3270.00 10-Jul-01 Forecast transaction Expenses:Books $10.00 $3280.00 10-Aug-01 Forecast transaction Expenses:Books $10.00 $3290.00 10-Sep-01 Forecast transaction Expenses:Books $10.00 $3300.00 10-Oct-01 Forecast transaction Expenses:Books $10.00 $3310.00 10-Nov-01 Forecast transaction Expenses:Books $10.00 $3320.00 10-Dec-01 Forecast transaction Expenses:Books $10.00 $3330.00 11-Jan-01 Forecast transaction Expenses:Books $10.00 $3340.00 11-Feb-01 Forecast transaction Expenses:Books $10.00 $3350.00 11-Mar-01 Forecast transaction Expenses:Books $10.00 $3360.00 11-Apr-01 Forecast transaction Expenses:Books $10.00 $3370.00 11-May-01 Forecast transaction Expenses:Books $10.00 $3380.00 11-Jun-01 Forecast transaction Expenses:Books $10.00 $3390.00 11-Jul-01 Forecast transaction Expenses:Books $10.00 $3400.00 11-Aug-01 Forecast transaction Expenses:Books $10.00 $3410.00 11-Sep-01 Forecast transaction Expenses:Books $10.00 $3420.00 11-Oct-01 Forecast transaction Expenses:Books $10.00 $3430.00 11-Nov-01 Forecast transaction Expenses:Books $10.00 $3440.00 11-Dec-01 Forecast transaction Expenses:Books $10.00 $3450.00 12-Jan-01 Forecast transaction Expenses:Books $10.00 $3460.00 12-Feb-01 Forecast transaction Expenses:Books $10.00 $3470.00 12-Mar-01 Forecast transaction Expenses:Books $10.00 $3480.00 12-Apr-01 Forecast transaction Expenses:Books $10.00 $3490.00 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-forecast-years.test��������������������������������������������������0000664�0000000�0000000�00000036402�14411236400�0022152�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly Expenses:Rent 500.00 GBP Assets test --now 2012-01-01 --forecast "T<200000.00 GBP" reg :rent 12-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 500.00 GBP 12-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 1000.00 GBP 12-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 1500.00 GBP 12-May-01 Forecast transaction Expenses:Rent 500.00 GBP 2000.00 GBP 12-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 2500.00 GBP 12-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 3000.00 GBP 12-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 3500.00 GBP 12-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 4000.00 GBP 12-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 4500.00 GBP 12-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 5000.00 GBP 12-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 5500.00 GBP 13-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 6000.00 GBP 13-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 6500.00 GBP 13-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 7000.00 GBP 13-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 7500.00 GBP 13-May-01 Forecast transaction Expenses:Rent 500.00 GBP 8000.00 GBP 13-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 8500.00 GBP 13-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 9000.00 GBP 13-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 9500.00 GBP 13-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 10000.00 GBP 13-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 10500.00 GBP 13-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 11000.00 GBP 13-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 11500.00 GBP 14-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 12000.00 GBP 14-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 12500.00 GBP 14-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 13000.00 GBP 14-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 13500.00 GBP 14-May-01 Forecast transaction Expenses:Rent 500.00 GBP 14000.00 GBP 14-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 14500.00 GBP 14-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 15000.00 GBP 14-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 15500.00 GBP 14-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 16000.00 GBP 14-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 16500.00 GBP 14-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 17000.00 GBP 14-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 17500.00 GBP 15-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 18000.00 GBP 15-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 18500.00 GBP 15-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 19000.00 GBP 15-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 19500.00 GBP 15-May-01 Forecast transaction Expenses:Rent 500.00 GBP 20000.00 GBP 15-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 20500.00 GBP 15-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 21000.00 GBP 15-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 21500.00 GBP 15-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 22000.00 GBP 15-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 22500.00 GBP 15-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 23000.00 GBP 15-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 23500.00 GBP 16-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 24000.00 GBP 16-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 24500.00 GBP 16-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 25000.00 GBP 16-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 25500.00 GBP 16-May-01 Forecast transaction Expenses:Rent 500.00 GBP 26000.00 GBP 16-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 26500.00 GBP 16-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 27000.00 GBP 16-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 27500.00 GBP 16-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 28000.00 GBP 16-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 28500.00 GBP 16-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 29000.00 GBP 16-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 29500.00 GBP end test test --now 2012-01-01 --forecast-years 1 --forecast "T<200000.00 GBP" reg :rent 12-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 500.00 GBP 12-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 1000.00 GBP 12-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 1500.00 GBP 12-May-01 Forecast transaction Expenses:Rent 500.00 GBP 2000.00 GBP 12-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 2500.00 GBP 12-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 3000.00 GBP 12-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 3500.00 GBP 12-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 4000.00 GBP 12-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 4500.00 GBP 12-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 5000.00 GBP 12-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 5500.00 GBP end test test --now 2012-01-01 --forecast-years 10 --forecast "T<200000.00 GBP" reg :rent 12-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 500.00 GBP 12-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 1000.00 GBP 12-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 1500.00 GBP 12-May-01 Forecast transaction Expenses:Rent 500.00 GBP 2000.00 GBP 12-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 2500.00 GBP 12-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 3000.00 GBP 12-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 3500.00 GBP 12-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 4000.00 GBP 12-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 4500.00 GBP 12-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 5000.00 GBP 12-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 5500.00 GBP 13-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 6000.00 GBP 13-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 6500.00 GBP 13-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 7000.00 GBP 13-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 7500.00 GBP 13-May-01 Forecast transaction Expenses:Rent 500.00 GBP 8000.00 GBP 13-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 8500.00 GBP 13-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 9000.00 GBP 13-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 9500.00 GBP 13-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 10000.00 GBP 13-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 10500.00 GBP 13-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 11000.00 GBP 13-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 11500.00 GBP 14-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 12000.00 GBP 14-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 12500.00 GBP 14-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 13000.00 GBP 14-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 13500.00 GBP 14-May-01 Forecast transaction Expenses:Rent 500.00 GBP 14000.00 GBP 14-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 14500.00 GBP 14-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 15000.00 GBP 14-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 15500.00 GBP 14-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 16000.00 GBP 14-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 16500.00 GBP 14-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 17000.00 GBP 14-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 17500.00 GBP 15-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 18000.00 GBP 15-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 18500.00 GBP 15-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 19000.00 GBP 15-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 19500.00 GBP 15-May-01 Forecast transaction Expenses:Rent 500.00 GBP 20000.00 GBP 15-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 20500.00 GBP 15-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 21000.00 GBP 15-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 21500.00 GBP 15-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 22000.00 GBP 15-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 22500.00 GBP 15-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 23000.00 GBP 15-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 23500.00 GBP 16-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 24000.00 GBP 16-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 24500.00 GBP 16-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 25000.00 GBP 16-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 25500.00 GBP 16-May-01 Forecast transaction Expenses:Rent 500.00 GBP 26000.00 GBP 16-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 26500.00 GBP 16-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 27000.00 GBP 16-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 27500.00 GBP 16-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 28000.00 GBP 16-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 28500.00 GBP 16-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 29000.00 GBP 16-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 29500.00 GBP 17-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 30000.00 GBP 17-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 30500.00 GBP 17-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 31000.00 GBP 17-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 31500.00 GBP 17-May-01 Forecast transaction Expenses:Rent 500.00 GBP 32000.00 GBP 17-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 32500.00 GBP 17-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 33000.00 GBP 17-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 33500.00 GBP 17-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 34000.00 GBP 17-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 34500.00 GBP 17-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 35000.00 GBP 17-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 35500.00 GBP 18-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 36000.00 GBP 18-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 36500.00 GBP 18-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 37000.00 GBP 18-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 37500.00 GBP 18-May-01 Forecast transaction Expenses:Rent 500.00 GBP 38000.00 GBP 18-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 38500.00 GBP 18-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 39000.00 GBP 18-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 39500.00 GBP 18-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 40000.00 GBP 18-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 40500.00 GBP 18-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 41000.00 GBP 18-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 41500.00 GBP 19-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 42000.00 GBP 19-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 42500.00 GBP 19-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 43000.00 GBP 19-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 43500.00 GBP 19-May-01 Forecast transaction Expenses:Rent 500.00 GBP 44000.00 GBP 19-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 44500.00 GBP 19-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 45000.00 GBP 19-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 45500.00 GBP 19-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 46000.00 GBP 19-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 46500.00 GBP 19-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 47000.00 GBP 19-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 47500.00 GBP 20-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 48000.00 GBP 20-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 48500.00 GBP 20-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 49000.00 GBP 20-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 49500.00 GBP 20-May-01 Forecast transaction Expenses:Rent 500.00 GBP 50000.00 GBP 20-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 50500.00 GBP 20-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 51000.00 GBP 20-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 51500.00 GBP 20-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 52000.00 GBP 20-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 52500.00 GBP 20-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 53000.00 GBP 20-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 53500.00 GBP 21-Jan-01 Forecast transaction Expenses:Rent 500.00 GBP 54000.00 GBP 21-Feb-01 Forecast transaction Expenses:Rent 500.00 GBP 54500.00 GBP 21-Mar-01 Forecast transaction Expenses:Rent 500.00 GBP 55000.00 GBP 21-Apr-01 Forecast transaction Expenses:Rent 500.00 GBP 55500.00 GBP 21-May-01 Forecast transaction Expenses:Rent 500.00 GBP 56000.00 GBP 21-Jun-01 Forecast transaction Expenses:Rent 500.00 GBP 56500.00 GBP 21-Jul-01 Forecast transaction Expenses:Rent 500.00 GBP 57000.00 GBP 21-Aug-01 Forecast transaction Expenses:Rent 500.00 GBP 57500.00 GBP 21-Sep-01 Forecast transaction Expenses:Rent 500.00 GBP 58000.00 GBP 21-Oct-01 Forecast transaction Expenses:Rent 500.00 GBP 58500.00 GBP 21-Nov-01 Forecast transaction Expenses:Rent 500.00 GBP 59000.00 GBP 21-Dec-01 Forecast transaction Expenses:Rent 500.00 GBP 59500.00 GBP end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-forecast_only.test���������������������������������������������������0000664�0000000�0000000�00000010734�14411236400�0022072�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly since 2010/01/01 Expenses:Bills:Rent $873.00 Expenses:Household $200.00 Income:Salary -$2491.60 Assets:Bank:Checking ~ biweekly from 2010/02/23 Expenses:Bills:Housecleaning $85.00 Assets:Bank:Checking test reg --forecast 'date <[2011]' --now=2010/06/21 10-Jul-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Jul-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Jul-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Jul-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Jun-27 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jun-27 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Jul-11 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jul-11 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Aug-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Aug-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Aug-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Aug-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Jul-25 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jul-25 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Aug-08 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Aug-08 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Sep-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Sep-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Sep-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Sep-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Aug-22 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Aug-22 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Sep-05 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Sep-05 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Oct-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Oct-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Oct-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Oct-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Sep-19 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Sep-19 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Oct-03 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Oct-03 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Nov-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Nov-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Nov-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Nov-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Oct-17 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Oct-17 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Oct-31 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Oct-31 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Nov-14 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Nov-14 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Dec-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Dec-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Dec-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Dec-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Nov-28 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Nov-28 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Dec-12 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Dec-12 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Dec-26 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Dec-26 Forecast transaction Assets:Bank:Checking $-85.00 0 end test ������������������������������������ledger-3.3.2/test/baseline/opt-format.test����������������������������������������������������������0000664�0000000�0000000�00000000430�14411236400�0020503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --format='%(account) = %(strip(amount))\n' Assets:Investments:Vanguard:VMMXX = 0.350 VMMXX Income:Dividends:Vanguard:VMMXX = $-0.35 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-gain.test������������������������������������������������������������0000664�0000000�0000000�00000004602�14411236400�0020136�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/01/01 Sample 1a Assets:Brokerage:Stocks 100 S Assets:Brokerage:Cash -100 P P 2009/01/15 00:00:00 S 2 P 2009/02/01 Sample 2a Assets:Brokerage:Stocks 100 S @ 1 P Assets:Brokerage:Cash P 2009/02/01 00:00:00 S 4 P 2009/03/01 Sample 3a Assets:Brokerage:Stocks 100 S @@ 100 P Assets:Brokerage:Cash P 2009/03/01 00:00:00 S 8 P 2009/04/01 Sample 4a Assets:Brokerage:Cash 100 P Assets:Brokerage:Stocks -100 S {1 P} P 2009/04/01 00:00:00 S 16 P ; In this usage case, the top amount is always secondary ; 2010/01/01 Sample 1b ; Assets:Brokerage:Cash -100 P ; Assets:Brokerage:Stocks 100 S ; ; P 2010/01/01 00:00:00 S 2 P 2010/02/01 Sample 2b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @ 1 P P 2010/02/01 00:00:00 S 4 P 2010/03/01 Sample 3b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @@ 100 P P 2010/03/01 00:00:00 S 8 P 2010/04/01 Sample 4b Assets:Brokerage:Stocks -100 S {1 P} Assets:Brokerage:Cash 100 P P 2010/04/01 00:00:00 S 16 P test reg --gain stocks 09-Jan-15 Commodities revalued <Revalued> 100 P 100 P 09-Feb-01 Commodities revalued <Revalued> 200 P 300 P 09-Feb-01 Sample 2a Asset:Brokerage:Stocks 300 P 600 P 09-Mar-01 Commodities revalued <Revalued> 800 P 1400 P 09-Mar-01 Sample 3a Asset:Brokerage:Stocks 700 P 2100 P 09-Apr-01 Commodities revalued <Revalued> 2400 P 4500 P 09-Apr-01 Sample 4a Asset:Brokerage:Stocks -1500 P 3000 P 10-Feb-01 Commodities revalued <Revalued> -2400 P 600 P 10-Feb-01 Sample 2b Asset:Brokerage:Stocks 300 P 900 P 10-Mar-01 Commodities revalued <Revalued> 1200 P 2100 P 10-Mar-01 Sample 3b Asset:Brokerage:Stocks 700 P 2800 P 10-Apr-01 Commodities revalued <Revalued> 3200 P 6000 P 10-Apr-01 Sample 4b Asset:Brokerage:Stocks -1500 P 4500 P end test test bal --change 4500 P Assets:Brokerage:Stocks end test test bal -G 4500 P Assets:Brokerage:Stocks end test ������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-group-by.test��������������������������������������������������������0000664�0000000�0000000�00000006354�14411236400�0020772�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-20 * Test GBP A -10.00 GBP B 2012-03-20 * Test EUR A -10.00 EUR B 2012-03-22 * Test GBP A -10.00 GBP B 2012-03-22 * Test EUR A -10.00 EUR B 2012-03-25 * Test GBP A -10.00 GBP B 2012-03-25 * Test EUR A -10.00 EUR B test reg --group-by payee Test EUR 12-Mar-20 Test EUR A -10.00 EUR -10.00 EUR B 10.00 EUR 0 12-Mar-22 Test EUR A -10.00 EUR -10.00 EUR B 10.00 EUR 0 12-Mar-25 Test EUR A -10.00 EUR -10.00 EUR B 10.00 EUR 0 Test GBP 12-Mar-20 Test GBP A -10.00 GBP -10.00 GBP B 10.00 GBP 0 12-Mar-22 Test GBP A -10.00 GBP -10.00 GBP B 10.00 GBP 0 12-Mar-25 Test GBP A -10.00 GBP -10.00 GBP B 10.00 GBP 0 end test test reg --group-by commodity EUR 12-Mar-20 Test EUR A -10.00 EUR -10.00 EUR B 10.00 EUR 0 12-Mar-22 Test EUR A -10.00 EUR -10.00 EUR B 10.00 EUR 0 12-Mar-25 Test EUR A -10.00 EUR -10.00 EUR B 10.00 EUR 0 GBP 12-Mar-20 Test GBP A -10.00 GBP -10.00 GBP B 10.00 GBP 0 12-Mar-22 Test GBP A -10.00 GBP -10.00 GBP B 10.00 GBP 0 12-Mar-25 Test GBP A -10.00 GBP -10.00 GBP B 10.00 GBP 0 end test test bal --group-by commodity EUR -30.00 EUR A 30.00 EUR B -------------------- 0 GBP -30.00 GBP A 30.00 GBP B -------------------- 0 end test test bal --group-by payee Test EUR -30.00 EUR A 30.00 EUR B -------------------- 0 Test GBP -30.00 GBP A 30.00 GBP B -------------------- 0 end test test bal --group-by date 2012/03/20 -10.00 EUR -10.00 GBP A 10.00 EUR 10.00 GBP B -------------------- 0 2012/03/22 -10.00 EUR -10.00 GBP A 10.00 EUR 10.00 GBP B -------------------- 0 2012/03/25 -10.00 EUR -10.00 GBP A 10.00 EUR 10.00 GBP B -------------------- 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-group-title-format.test����������������������������������������������0000664�0000000�0000000�00000001531�14411236400�0022757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-20 * Test GBP A -10.00 GBP B 2012-03-20 * Test EUR A -10.00 EUR B 2012-03-22 * Test GBP A -10.00 GBP B 2012-03-22 * Test EUR A -10.00 EUR B test bal --group-by payee --group-title-format "-%(value)-\n" -Test EUR- -20.00 EUR A 20.00 EUR B -------------------- 0 -Test GBP- -20.00 GBP A 20.00 GBP B -------------------- 0 end test test bal --group-by date --group-title-format "|%(value)|\n" |2012/03/20| -10.00 EUR -10.00 GBP A 10.00 EUR 10.00 GBP B -------------------- 0 |2012/03/22| -10.00 EUR -10.00 GBP A 10.00 EUR 10.00 GBP B -------------------- 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-head.test������������������������������������������������������������0000664�0000000�0000000�00000012153�14411236400�0020121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --head=10 books 08-Jan-01 January Expenses:Books $10.00 $10.00 08-Jan-31 End of January Expenses:Books $10.00 $20.00 08-Feb-01 February Expenses:Books $20.00 $40.00 08-Feb-28 End of February Expenses:Books $20.00 $60.00 08-Mar-01 March Expenses:Books $30.00 $90.00 08-Mar-31 End of March Expenses:Books $30.00 $120.00 08-Apr-01 April Expenses:Books $40.00 $160.00 08-Apr-30 End of April Expenses:Books $40.00 $200.00 08-May-01 May Expenses:Books $50.00 $250.00 08-May-31 End of May Expenses:Books $50.00 $300.00 end test test reg --first=10 books 08-Jan-01 January Expenses:Books $10.00 $10.00 08-Jan-31 End of January Expenses:Books $10.00 $20.00 08-Feb-01 February Expenses:Books $20.00 $40.00 08-Feb-28 End of February Expenses:Books $20.00 $60.00 08-Mar-01 March Expenses:Books $30.00 $90.00 08-Mar-31 End of March Expenses:Books $30.00 $120.00 08-Apr-01 April Expenses:Books $40.00 $160.00 08-Apr-30 End of April Expenses:Books $40.00 $200.00 08-May-01 May Expenses:Books $50.00 $250.00 08-May-31 End of May Expenses:Books $50.00 $300.00 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-historical.test������������������������������������������������������0000664�0000000�0000000�00000031506�14411236400�0021364�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D EUR 2.000,00 P 2011-12-15 $ EUR 2 P 2011-12-15 AAPL $5.00 2012-01-01 Broker Assets:Stocks 10 AAPL {$1} @ $10 Equity P 2012-01-15 AAPL $15.00 2012-02-02 Broker Assets:Stocks 10 AAPL {$2} @ $20 Equity P 2012-02-15 AAPL $25.00 2012-03-03 Broker Assets:Stocks 10 AAPL {$3} @ $30 Equity P 2012-03-15 AAPL $35.00 2012-04-04 Broker Assets:Stocks 10 AAPL {$4} @ $40 Equity P 2012-04-15 AAPL $45.00 2012-05-05 Broker Assets:Stocks 10 AAPL {$5} @ $50 Equity P 2012-5-15 AAPL $55.00 test reg stocks 12-Jan-01 Broker Assets:Stocks 10 AAPL 10 AAPL 12-Feb-02 Broker Assets:Stocks 10 AAPL 20 AAPL 12-Mar-03 Broker Assets:Stocks 10 AAPL 30 AAPL 12-Apr-04 Broker Assets:Stocks 10 AAPL 40 AAPL 12-May-05 Broker Assets:Stocks 10 AAPL 50 AAPL end test test reg stocks -O 12-Jan-01 Broker Assets:Stocks 10 AAPL 10 AAPL 12-Feb-02 Broker Assets:Stocks 10 AAPL 20 AAPL 12-Mar-03 Broker Assets:Stocks 10 AAPL 30 AAPL 12-Apr-04 Broker Assets:Stocks 10 AAPL 40 AAPL 12-May-05 Broker Assets:Stocks 10 AAPL 50 AAPL end test test reg stocks -B 12-Jan-01 Broker Assets:Stocks $10 $10 12-Feb-02 Broker Assets:Stocks $20 $30 12-Mar-03 Broker Assets:Stocks $30 $60 12-Apr-04 Broker Assets:Stocks $40 $100 12-May-05 Broker Assets:Stocks $50 $150 end test test reg stocks -I 12-Jan-01 Broker Assets:Stocks $10 $10 12-Feb-02 Broker Assets:Stocks $20 $30 12-Mar-03 Broker Assets:Stocks $30 $60 12-Apr-04 Broker Assets:Stocks $40 $100 12-May-05 Broker Assets:Stocks $50 $150 end test test reg stocks -V --now=2012/05/10 12-Jan-01 Broker Assets:Stocks $100 $100 12-Jan-15 Commodities revalued <Revalued> $50 $150 12-Feb-02 Commodities revalued <Revalued> $50 $200 12-Feb-02 Broker Assets:Stocks $200 $400 12-Feb-15 Commodities revalued <Revalued> $100 $500 12-Mar-03 Commodities revalued <Revalued> $100 $600 12-Mar-03 Broker Assets:Stocks $300 $900 12-Mar-15 Commodities revalued <Revalued> $150 $1050 12-Apr-04 Commodities revalued <Revalued> $150 $1200 12-Apr-04 Broker Assets:Stocks $400 $1600 12-Apr-15 Commodities revalued <Revalued> $200 $1800 12-May-05 Commodities revalued <Revalued> $200 $2000 12-May-05 Broker Assets:Stocks $500 $2500 end test test reg stocks -O -V --now=2012/05/10 12-Jan-01 Broker Assets:Stocks $100 $100 12-Jan-15 Commodities revalued <Revalued> $50 $150 12-Feb-02 Commodities revalued <Revalued> $50 $200 12-Feb-02 Broker Assets:Stocks $200 $400 12-Feb-15 Commodities revalued <Revalued> $100 $500 12-Mar-03 Commodities revalued <Revalued> $100 $600 12-Mar-03 Broker Assets:Stocks $300 $900 12-Mar-15 Commodities revalued <Revalued> $150 $1050 12-Apr-04 Commodities revalued <Revalued> $150 $1200 12-Apr-04 Broker Assets:Stocks $400 $1600 12-Apr-15 Commodities revalued <Revalued> $200 $1800 12-May-05 Commodities revalued <Revalued> $200 $2000 12-May-05 Broker Assets:Stocks $500 $2500 end test test reg stocks -B -V 12-Jan-01 Broker Assets:Stocks $10 $10 12-Feb-02 Broker Assets:Stocks $20 $30 12-Mar-03 Broker Assets:Stocks $30 $60 12-Apr-04 Broker Assets:Stocks $40 $100 12-May-05 Broker Assets:Stocks $50 $150 end test test reg stocks -I -V 12-Jan-01 Broker Assets:Stocks $10 $10 12-Feb-02 Broker Assets:Stocks $20 $30 12-Mar-03 Broker Assets:Stocks $30 $60 12-Apr-04 Broker Assets:Stocks $40 $100 12-May-05 Broker Assets:Stocks $50 $150 end test test reg stocks -X EUR --now=2012/05/10 12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 12-Feb-02 Commodities revalued <Revalued> EUR 200,00 EUR 400,00 12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 800,00 12-Mar-03 Commodities revalued <Revalued> EUR 400,00 EUR 1.200,00 12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.800,00 12-Apr-04 Commodities revalued <Revalued> EUR 600,00 EUR 2.400,00 12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 3.200,00 12-May-05 Commodities revalued <Revalued> EUR 800,00 EUR 4.000,00 12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 5.000,00 end test test reg stocks -O -X EUR --now=2012/05/10 12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 12-Feb-02 Commodities revalued <Revalued> EUR 200,00 EUR 400,00 12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 800,00 12-Mar-03 Commodities revalued <Revalued> EUR 400,00 EUR 1.200,00 12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.800,00 12-Apr-04 Commodities revalued <Revalued> EUR 600,00 EUR 2.400,00 12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 3.200,00 12-May-05 Commodities revalued <Revalued> EUR 800,00 EUR 4.000,00 12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 5.000,00 end test test reg stocks -B -X EUR 12-Jan-01 Broker Assets:Stocks EUR 20,00 EUR 20,00 12-Feb-02 Broker Assets:Stocks EUR 40,00 EUR 60,00 12-Mar-03 Broker Assets:Stocks EUR 60,00 EUR 120,00 12-Apr-04 Broker Assets:Stocks EUR 80,00 EUR 200,00 12-May-05 Broker Assets:Stocks EUR 100,00 EUR 300,00 end test test reg stocks -I -X EUR 12-Jan-01 Broker Assets:Stocks EUR 20,00 EUR 20,00 12-Feb-02 Broker Assets:Stocks EUR 40,00 EUR 60,00 12-Mar-03 Broker Assets:Stocks EUR 60,00 EUR 120,00 12-Apr-04 Broker Assets:Stocks EUR 80,00 EUR 200,00 12-May-05 Broker Assets:Stocks EUR 100,00 EUR 300,00 end test test reg stocks -H 12-Jan-01 Broker Assets:Stocks $100 $100 12-Feb-02 Broker Assets:Stocks $200 $300 12-Mar-03 Broker Assets:Stocks $300 $600 12-Apr-04 Broker Assets:Stocks $400 $1000 12-May-05 Broker Assets:Stocks $500 $1500 end test test reg stocks -O -H 12-Jan-01 Broker Assets:Stocks $100 $100 12-Feb-02 Broker Assets:Stocks $200 $300 12-Mar-03 Broker Assets:Stocks $300 $600 12-Apr-04 Broker Assets:Stocks $400 $1000 12-May-05 Broker Assets:Stocks $500 $1500 end test test reg stocks -B -H 12-Jan-01 Broker Assets:Stocks $10 $10 12-Feb-02 Broker Assets:Stocks $20 $30 12-Mar-03 Broker Assets:Stocks $30 $60 12-Apr-04 Broker Assets:Stocks $40 $100 12-May-05 Broker Assets:Stocks $50 $150 end test test reg stocks -I -H 12-Jan-01 Broker Assets:Stocks $10 $10 12-Feb-02 Broker Assets:Stocks $20 $30 12-Mar-03 Broker Assets:Stocks $30 $60 12-Apr-04 Broker Assets:Stocks $40 $100 12-May-05 Broker Assets:Stocks $50 $150 end test test reg stocks -H -V 12-Jan-01 Broker Assets:Stocks $100 $100 12-Feb-02 Broker Assets:Stocks $200 $300 12-Mar-03 Broker Assets:Stocks $300 $600 12-Apr-04 Broker Assets:Stocks $400 $1000 12-May-05 Broker Assets:Stocks $500 $1500 end test test reg stocks -O -H -V 12-Jan-01 Broker Assets:Stocks $100 $100 12-Feb-02 Broker Assets:Stocks $200 $300 12-Mar-03 Broker Assets:Stocks $300 $600 12-Apr-04 Broker Assets:Stocks $400 $1000 12-May-05 Broker Assets:Stocks $500 $1500 end test test reg stocks -B -H -V 12-Jan-01 Broker Assets:Stocks $10 $10 12-Feb-02 Broker Assets:Stocks $20 $30 12-Mar-03 Broker Assets:Stocks $30 $60 12-Apr-04 Broker Assets:Stocks $40 $100 12-May-05 Broker Assets:Stocks $50 $150 end test test reg stocks -I -H -V 12-Jan-01 Broker Assets:Stocks $10 $10 12-Feb-02 Broker Assets:Stocks $20 $30 12-Mar-03 Broker Assets:Stocks $30 $60 12-Apr-04 Broker Assets:Stocks $40 $100 12-May-05 Broker Assets:Stocks $50 $150 end test test reg stocks -H -X EUR 12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 600,00 12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.200,00 12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 2.000,00 12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 3.000,00 end test test reg stocks -O -H -X EUR 12-Jan-01 Broker Assets:Stocks EUR 200,00 EUR 200,00 12-Feb-02 Broker Assets:Stocks EUR 400,00 EUR 600,00 12-Mar-03 Broker Assets:Stocks EUR 600,00 EUR 1.200,00 12-Apr-04 Broker Assets:Stocks EUR 800,00 EUR 2.000,00 12-May-05 Broker Assets:Stocks EUR 1.000,00 EUR 3.000,00 end test test reg stocks -B -H -X EUR 12-Jan-01 Broker Assets:Stocks EUR 20,00 EUR 20,00 12-Feb-02 Broker Assets:Stocks EUR 40,00 EUR 60,00 12-Mar-03 Broker Assets:Stocks EUR 60,00 EUR 120,00 12-Apr-04 Broker Assets:Stocks EUR 80,00 EUR 200,00 12-May-05 Broker Assets:Stocks EUR 100,00 EUR 300,00 end test test reg stocks -I -H -X EUR 12-Jan-01 Broker Assets:Stocks EUR 20,00 EUR 20,00 12-Feb-02 Broker Assets:Stocks EUR 40,00 EUR 60,00 12-Mar-03 Broker Assets:Stocks EUR 60,00 EUR 120,00 12-Apr-04 Broker Assets:Stocks EUR 80,00 EUR 200,00 12-May-05 Broker Assets:Stocks EUR 100,00 EUR 300,00 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-immediate.test�������������������������������������������������������0000664�0000000�0000000�00000001556�14411236400�0021163�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 EUR 2012-01-01 * Test Assets:Investments 1 AAA @@ 10.00 EUR Assets:Investments 1 BBB @@ 20.00 EUR Equity:Opening balance P 2012-07-01 AAA 10.123 EUR P 2012-07-01 BBB 20.123 EUR test bal -V --unrealized 30.25 EUR Assets:Investments -30.25 EUR Equity -30.00 EUR Opening balance -0.25 EUR Unrealized Gains -------------------- 0 end test test bal -V --immediate 30.00 EUR Assets:Investments -30.00 EUR Equity:Opening balance -------------------- 0 end test test reg -V --immediate 12-Jan-01 Test Assets:Investments 10.00 EUR 10.00 EUR Assets:Investments 20.00 EUR 30.00 EUR Equity:Opening balance -30.00 EUR 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-init-file.dat��������������������������������������������������������0000664�0000000�0000000�00000000020�14411236400�0020657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--decimal-comma ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-init-file.test�������������������������������������������������������0000664�0000000�0000000�00000000377�14411236400�0021105�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-17 Quick Expenses:Food 12,50 € Assets:Cash test --init-file test/baseline/opt-init-file.dat bal -12,50 € Assets:Cash 12,50 € Expenses:Food -------------------- 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-inject.test����������������������������������������������������������0000664�0000000�0000000�00000000501�14411236400�0020466�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-20 * Test GBP ; Expected:: -15.00 GBP Expenses:Phone 20.00 GBP Assets:Cash test --inject Expected reg Expenses:Phone 12-Mar-20 Test GBP Expected -15.00 GBP -15.00 GBP 12-Mar-20 Test GBP Expenses:Phone 20.00 GBP 5.00 GBP end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-input-date-format.test�����������������������������������������������0000664�0000000�0000000�00000000650�14411236400�0022557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������02%02%2007 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --input-date-format='%m%%%d%%%Y' 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ����������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-invert.test����������������������������������������������������������0000664�0000000�0000000�00000000620�14411236400�0020523�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --invert 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX -0.350 VMMXX -0.350 VMMXX In:Divid:Vanguar:VMMXX $0.35 $0.35 -0.350 VMMXX end test ����������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-limit.test�����������������������������������������������������������0000664�0000000�0000000�00000011437�14411236400�0020342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --limit='account =~ /Books/ and amount < 50' 08-Jan-01 January Expenses:Books $10.00 $10.00 08-Jan-31 End of January Expenses:Books $10.00 $20.00 08-Feb-01 February Expenses:Books $20.00 $40.00 08-Feb-28 End of February Expenses:Books $20.00 $60.00 08-Mar-01 March Expenses:Books $30.00 $90.00 08-Mar-31 End of March Expenses:Books $30.00 $120.00 08-Apr-01 April Expenses:Books $40.00 $160.00 08-Apr-30 End of April Expenses:Books $40.00 $200.00 09-Jan-01 January Expenses:Books $10.00 $210.00 09-Jan-31 End of January Expenses:Books $10.00 $220.00 09-Feb-01 February Expenses:Books $20.00 $240.00 09-Feb-28 End of February Expenses:Books $20.00 $260.00 09-Mar-01 March Expenses:Books $30.00 $290.00 09-Mar-31 End of March Expenses:Books $30.00 $320.00 09-Apr-01 April Expenses:Books $40.00 $360.00 09-Apr-30 End of April Expenses:Books $40.00 $400.00 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-lot-dates.test�������������������������������������������������������0000664�0000000�0000000�00001131211�14411236400�0021112�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������C 1.00s = 100c C 1.00G = 100s D 1.00G 2006/03/14 Opening Balances Assets:Tajer 1339829c Assets:Gruulmorg 248720c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1428c Expenses:Fees:Auction 768c Expenses:Fees:Auction 612c Expenses:Fees:Auction 4764c Expenses:Fees:Auction 3372c Expenses:Fees:Auction 1296c Expenses:Fees:Auction 1332c Expenses:Fees:Auction 660c Expenses:Fees:Auction 10044c Expenses:Fees:Auction 3588c Expenses:Fees:Auction 1632c Expenses:Fees:Auction 8388c Expenses:Fees:Auction 9984c Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 158860c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1320c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 11496c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 3216c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/14 Auction House Assets:Tajer 34678c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Gruulmorg 1G Equity:Gold 2006/03/14 Auction House Assets:Tajer 59389c Equity:Gold 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Player Assets:Tajer 3G Equity:Gold 2006/03/14 Player Assets:Tajer 6G Equity:Gold 2006/03/14 Auction House Expenses:Items 35s Assets:Tajer 2006/03/14 Auction House Assets:Tajer:Items "Plans: Wildthorn Mail" 1 @ 125s Assets:Tajer 2006/03/14 Auction House Assets:Bids 259c Assets:Bids 45s Assets:Bids 4720c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Auction House Expenses:Items 8G Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Puldoost Assets:Tajer 8G Expenses:Items 2006/03/14 Auction House Assets:Wyshona:Items "Plans: Wildthorn Mail" 1 {1.25G} Assets:Tajer:Items 2006/03/15 Auction House Assets:Tajer 45s Assets:Tajer 259c Assets:Bids 2006/03/15 Auction House Assets:Tajer 4720c Assets:Bids 2006/03/15 Auction House Expenses:Fees:Auction 12542c ; something got lost here Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 2375c Expenses:Fees:Auction -2375c Assets:Gruulmorg 2006/03/15 Auction House Assets:Danell 4c Equity:Gold 2006/03/15 Transfer Assets:Gruulmorg 2c Assets:Danell 2006/03/15 Transfer Assets:Danell 4250c Expenses:Fees:Auction 750c Assets:Gruulmorg -50s 2006/03/15 Post Expenses:Fees:Mail 60c Assets:Danell 2006/03/15 Player Assets:Tajer 3G Equity:Gold 2006/03/15 Post Assets:Wyshona 40s Expenses:Fees:Mail 30c Assets:Danell 2006/03/15 Auction House Expenses:Fees:Auction 8s Expenses:Fees:Auction 11s Assets:Wyshona 2006/03/15 Auction House Assets:Tajer:Items "Beaststalker's Belt" 1 @ 65G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Vendor Assets:Tajer 16744c Assets:Tajer 16640c Equity:Gold 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 772c Expenses:Fees:Auction 544c Expenses:Fees:Auction 444c Expenses:Fees:Auction 432c Expenses:Fees:Auction 204c Assets:Tajer 2006/03/15 Player Assets:Tajer 12s Equity:Gold 2006/03/15 Vendor Assets:Tajer 22s Equity:Gold 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 1G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 21050c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 23000c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 150s Assets:Tajer 2006/03/16 Player Assets:Tajer 3G Equity:Gold 2006/03/16 Post Expenses:Fees:Mail 90c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 1195768c Assets:Tajer:Items "Beaststalker's Belt" -1 {65G} @ 1195768c Income:Brokering -545768c 2006/03/16 Auction House Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {21050c} Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {2.3G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1.5G} Assets:Tajer:Items 2006/03/16 Player Assets:Tajer 4G Equity:Gold 2006/03/16 Auction House Assets:Wyshona 1341s Equity:Gold 2006/03/16 Auction House Assets:Gruulmorg 4c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 4c Expenses:Fees:Mail 120c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 24s Expenses:Fees:Auction 12s Expenses:Fees:Auction 12s Expenses:Fees:Auction 84c Expenses:Fees:Auction 84c Assets:Wyshona 2006/03/16 Crazy Cat Lady Expenses:Items 40s Expenses:Items 40s Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 60c Assets:Danell 5G Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Tajer 20G Assets:Gruulmorg 2006/03/16 Auction House Assets:Tajer:Items "Pulsating Hydra Heart" 1 @ 1G Assets:Tajer 2006/03/16 Auction House Expenses:Fees:Auction 936c Assets:Tajer 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Gruulmorg 30G Assets:Tajer 2006/03/16 Auction House Assets:Gruulmorg:Items "Ace of Warlords" 2 @ 15G Assets:Gruulmorg 2006/03/16 Transfer Assets:Tajer:Items "Ace of Warlords" 2 {15G} Assets:Gruulmorg:Items 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 2104c Equity:Gold 2006/03/16 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/16 Transfer Assets:Danell 6c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Tajer 2006/03/16 General Goods Vendor Expenses:Items 50c ; wrapping paper Assets:Tajer 2006/03/16 Player Assets:Tajer 1G Equity:Gold 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1.5G} @ 180584c Income:Brokering -165584c 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1G} @ 180584c Income:Brokering -170584c 2006/03/17 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Player: raev Assets:Tajer:Items "Wildheart Belt" 1 @ 30G Assets:Tajer:Items "Ace of Warlords" -2 @ 15G 2006/03/17 Auction House Expenses:Fees:Auction 7482c Assets:Tajer 2006/03/17 Post Expenses:Fees:Mail 300c Assets:Wyshona 2006/03/17 Player Assets:Wyshona 1G Assets:Wyshona:Items "Plans: Wildthorn Mail" -1 {1.25G} @ 1G Expenses:Capital Loss 25s 2006/03/17 Auction House (implicit transfer) Expenses:Items 279s ; Recipe: Swiftness Potion Assets:Wyshona 2006/03/17 Auction House (implicit transfer) Assets:Danell:Items "Ace of Warlords" 1 @ 3G Assets:Tajer:Items "Ace of Warlords" 1 @ 3.9G Assets:Tajer:Items "Holy Bologna" 1 @ 2G Assets:Tajer:Items "The Emerald Dream" 1 @ 4G Assets:Tajer:Items "The Arcanist's Cookbook" 1 @ 4G Assets:Tajer:Items "Harnessing Shadows" 1 @ 5G Assets:Tajer:Items "Garona: Book on Treachery" 1 @ 4G Assets:Tajer:Items "Preserved Holly" 5 @ 20s Assets:Wyshona 2006/03/17 Auction House Assets:Tajer 4G Assets:Tajer:Items "Pulsating Hydra Heart" -1 {1G} @ 4G Income:Brokering -3G 2006/03/17 Auction House Assets:Danell 3171c Assets:Danell:Items "Ace of Warlords" -1 {3G} @ 3171c Expenses:Capital Loss 26829c 2006/03/17 Auction House Expenses:Fees:Auction 12537c Assets:Danell 2006/03/17 Transfer Assets:Gruulmorg 15G Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Auction House Assets:Wyshona 362450c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {21050c} @ 181225c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {2.3G} @ 181225c Income:Brokering -318400c 2006/03/17 Transfer Assets:Danell 499560c Expenses:Gifts 1G Expenses:Fees:Mail 30c Assets:Wyshona 2006/03/17 Post Expenses:Fees:Mail 90c Expenses:Fees:Auction 166c Assets:Gruulmorg 2006/03/17 Transfer Assets:Gruulmorg 459211c Expenses:Fees:Auction 81023c Assets:Danell -540234c 2006/03/17 Transfer Assets:Tajer 662465c Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/17 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Mail 30c Assets:Tajer 2006/03/18 Auction House Assets:Tajer 15G Assets:Tajer:Items "Ace of Warlords" -1 {3.9G} @ 15G Income:Brokering -111000c 2006/03/18 Auction House Assets:Tajer 434472c Assets:Tajer:Items "Wildheart Belt" -1 {30G} @ 434472c Income:Brokering -134472c 2006/03/18 Auction House Assets:Tajer 1995s Assets:Tajer:Items "Harnessing Shadows" -1 {5G} @ 1995s Income:Brokering -1495s 2006/03/19 Auction House Assets:Tajer 2850s Assets:Tajer:Items "Garona: Book on Treachery" -1 {4G} @ 2850s Income:Brokering -2450s 2006/03/19 Auction House Assets:Tajer 1710s Assets:Tajer:Items "The Arcanist's Cookbook" -1 {4G} @ 1710s Income:Brokering -1310s 2006/03/19 Auction House Assets:Tajer 46550c Assets:Tajer:Items "Preserved Holly" -5 {20s} @ 9310c Income:Brokering -36550c 2006/03/19 Auction House Assets:Tajer:Items "Two of Portals" 1 @ 3G Assets:Tajer:Items "Two of Portals" 1 @ 2.5G Assets:Tajer 2006/03/20 Auction House Assets:Tajer 163443c Assets:Tajer:Items "Holy Bologna" -1 {2G} @ 163443c Income:Brokering -143443c 2006/03/20 Auction House Assets:Tajer 5G Assets:Tajer:Items "Two of Portals" -1 {3G} @ 5G Income:Brokering -2G 2006/03/20 Auction House Assets:Tajer 15G Assets:Tajer:Items "The Emerald Dream" -1 {4G} @ 15G Income:Brokering -11G 2006/03/20 Auction House Expenses:Fees:Mail 60c Assets:Tajer 2006/03/21 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 170G Assets:Tajer 2006/03/21 Auction House Expenses:Fees:Auction 2760c Expenses:Fees:Auction 2760c Assets:Tajer 2006/03/22 Auction House Assets:Tajer:Items "Nightblade" 1 @ 200G Assets:Tajer 2006/03/22 Auction House Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/23 Auction House Assets:Tajer 1665260c Assets:Tajer:Items "Orb of Deception" -1 {170G} @ 1665260c Expenses:Capital Loss 34740c 2006/03/26 Auction House Assets:Tajer 81980c Assets:Tajer:Items "Two of Portals" -1 {2.5G} @ 81980c Income:Brokering -56980c 2006/03/26 Player Expenses:Items 150s ; Recipe: Elixir of Minor Agility Expenses:Fees:Mail 30c Expenses:Fees:Mail 30c Assets:Tajer 2006/03/27 Player Assets:Tajer 160G Assets:Tajer:Items "Nightblade" -1 {200G} @ 160G Expenses:Capital Loss 40G 2006/03/27 Player Expenses:Fees:Mail 30c Assets:Tajer 2006/03/26 Player Expenses:Items (9G * 6) ; Traveler's backpacks Expenses:Items 10G Expenses:Fees:Bank 10G Expenses:Fees:Mail 630c Expenses:Fees:Mail 330c Expenses:Fees:Mail 30c Assets:Tajer 2006/04/01 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 155G Assets:Tajer test reg -F '%(justify(scrub(total_expr), 80, 80, true))\n' --lot-dates --date-format %Y/%m/%d 133.98G 158.85G 0 14.28s 21.96s 28.08s 75.72s 1.09G 1.22G 1.36G 1.42G 2.43G 2.79G 2.95G 3.79G 4.79G 5.02G 0 15.89G 0 13.20s 0 1.15G 0 32.16s 0 30c 0 3.47G 0 23.16s 0 1.00G 0 5.94G 0 1.20s 0 3.00G 0 6.00G 0 35.00s 0 "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.22G "Plans: Wildthorn Mail" 1 [2006/03/14] "Plans: Wildthorn Mail" 1 [2006/03/14] -77.41s "Plans: Wildthorn Mail" 1 [2006/03/14] -30.21s -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.24G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] 6.75G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.24G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] 6.75G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Plans: Wildthorn Mail" 1 [2006/03/14] -80.00s "Plans: Wildthorn Mail" 1 [2006/03/14] -77.41s -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Plans: Wildthorn Mail" 1 [2006/03/14] -77.80s -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Plans: Wildthorn Mail" 1 [2006/03/14] 42c -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.01G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Plans: Wildthorn Mail" 1 [2006/03/14] -82.50s "Plans: Wildthorn Mail" 1 [2006/03/14] -75.00s -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.24G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] 1.75G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Plans: Wildthorn Mail" 1 [2006/03/14] -85.00s "Plans: Wildthorn Mail" 1 [2006/03/14] -84.70s -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.17G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.06G "Plans: Wildthorn Mail" 1 [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -1.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -65.42G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -64.58G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -62.91G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.17G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.12G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.07G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.03G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.01G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.13G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.03G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Beaststalker's Belt" 1 [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -67.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -67.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -67.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -66.42G "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -67.25G "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -67.25G "Plans: Mithril Shield Spike" 1 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -69.36G "Plans: Mithril Shield Spike" 1 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -69.36G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -71.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -71.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -73.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -70.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -73.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -73.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] -73.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" 1 [2006/03/15] 46.42G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 46.42G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 1 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -4.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 5.25G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.14G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -7.92G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -7.80G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -7.68G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -7.67G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -7.66G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -7.75G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -7.36G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -3.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 11.85G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -9.06G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 20.85G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.14G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -38.94G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -38.40G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -37.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -38.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -21.10G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -21.10G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -1 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -37.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -1 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -19.60G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -1 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -19.60G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.91G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -36.62G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.65G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -32.62G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -2 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Ace of Warlords" -1 [2006/03/17] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -35.40G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -62.31G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -58.31G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -58.31G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -61.31G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -60.99G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -60.99G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -58.31G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -57.05G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -58.31G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -43.31G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -43.30G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -58.31G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -22.06G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -22.06G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -1 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -22.06G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.90G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -3.94G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -2.94G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -2.94G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.90G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.89G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.87G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.90G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -7.98G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] 12.34s "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.90G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 12.35G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 12.35G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.90G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.15G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.15G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -53.90G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -1 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -38.90G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -38.90G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -50.00G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -6.55G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -6.55G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -20.00G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] -5.00s "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] -5.00s "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -15.00G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 13.50G "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 13.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -11.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 6.10G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 6.10G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -7.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -2.35G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -2.35G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -6.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -6.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" 1 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -6.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -11.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 4.84G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 4.84G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -9.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -4.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -4.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -6.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 8.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 8.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -2.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -2.49G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -2.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -2.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -172.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -172.22G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -171.95G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -172.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -172.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -372.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -370.73G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -368.96G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -367.19G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -365.42G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -364.67G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -372.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -205.97G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -205.97G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -202.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -194.30G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -1 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -194.30G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -200.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -198.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -198.50G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -198.49G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -200.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -40.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -40.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] 30c "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 54.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 64.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 74.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 74.06G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 74.10G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] 74.10G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Orb of Deception" 1 [2006/04/01] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] "Ace of Warlords" -2 "Ace of Warlords" 2 [2006/03/16] "Beaststalker's Belt" -1 "Beaststalker's Belt" 1 [2006/03/15] -155.00G "Garona: Book on Treachery" -1 "Garona: Book on Treachery" 1 [2006/03/17] "Harnessing Shadows" -1 "Harnessing Shadows" 1 [2006/03/17] "Holy Bologna" -1 "Holy Bologna" 1 [2006/03/17] Nightblade -1 Nightblade 1 [2006/03/22] "Orb of Deception" -1 "Orb of Deception" 1 [2006/03/21] "Orb of Deception" 1 [2006/04/01] "Plans: Mithril Shield Spike" -2 "Plans: Mithril Shield Spike" 2 [2006/03/15] "Plans: Wildthorn Mail" -1 "Plans: Wildthorn Mail" 1 [2006/03/14] "Preserved Holly" -5 "Preserved Holly" 5 [2006/03/17] "Pulsating Hydra Heart" -1 "Pulsating Hydra Heart" 1 [2006/03/16] "Recipe: Elixir of Giant Growth" -2 "Recipe: Elixir of Giant Growth" 2 [2006/03/15] "The Arcanist's Cookbook" -1 "The Arcanist's Cookbook" 1 [2006/03/17] "The Emerald Dream" -1 "The Emerald Dream" 1 [2006/03/17] "Two of Portals" -2 "Two of Portals" 2 [2006/03/19] "Wildheart Belt" -1 "Wildheart Belt" 1 [2006/03/17] end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-lot-notes.test�������������������������������������������������������0000664�0000000�0000000�00000002127�14411236400�0021144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Opening balance Assets:Cash 100.00 GBP Equity:Opening Balance 2012-01-02 * Voucher 1 Assets:Voucher 10.00 GBP (aaaa) Assets:Cash -10.00 GBP 2012-01-03 * Voucher 1 Assets:Voucher 10.00 GBP (bbbb) Assets:Cash -10.00 GBP 2012-01-04 * Voucher 1 Assets:Voucher 10.00 GBP (cccc) Assets:Cash -10.00 GBP test bal assets:voucher --lot-notes 10.00 GBP (aaaa) 10.00 GBP (bbbb) 10.00 GBP (cccc) Assets:Voucher end test test reg assets:voucher --lot-notes 12-Jan-02 Voucher 1 Assets:Voucher 10.00 GBP (aaaa) 10.00 GBP (aaaa) 12-Jan-03 Voucher 1 Assets:Voucher 10.00 GBP (bbbb) 10.00 GBP (aaaa) 10.00 GBP (bbbb) 12-Jan-04 Voucher 1 Assets:Voucher 10.00 GBP (cccc) 10.00 GBP (aaaa) 10.00 GBP (bbbb) 10.00 GBP (cccc) end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-lot-prices.test������������������������������������������������������0000664�0000000�0000000�00000410564�14411236400�0021311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������C 1.00s = 100c C 1.00G = 100s D 1.00G 2006/03/14 Opening Balances Assets:Tajer 1339829c Assets:Gruulmorg 248720c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1428c Expenses:Fees:Auction 768c Expenses:Fees:Auction 612c Expenses:Fees:Auction 4764c Expenses:Fees:Auction 3372c Expenses:Fees:Auction 1296c Expenses:Fees:Auction 1332c Expenses:Fees:Auction 660c Expenses:Fees:Auction 10044c Expenses:Fees:Auction 3588c Expenses:Fees:Auction 1632c Expenses:Fees:Auction 8388c Expenses:Fees:Auction 9984c Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 158860c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1320c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 11496c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 3216c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/14 Auction House Assets:Tajer 34678c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Gruulmorg 1G Equity:Gold 2006/03/14 Auction House Assets:Tajer 59389c Equity:Gold 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Player Assets:Tajer 3G Equity:Gold 2006/03/14 Player Assets:Tajer 6G Equity:Gold 2006/03/14 Auction House Expenses:Items 35s Assets:Tajer 2006/03/14 Auction House Assets:Tajer:Items "Plans: Wildthorn Mail" 1 @ 125s Assets:Tajer 2006/03/14 Auction House Assets:Bids 259c Assets:Bids 45s Assets:Bids 4720c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Auction House Expenses:Items 8G Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Puldoost Assets:Tajer 8G Expenses:Items 2006/03/14 Auction House Assets:Wyshona:Items "Plans: Wildthorn Mail" 1 {1.25G} Assets:Tajer:Items 2006/03/15 Auction House Assets:Tajer 45s Assets:Tajer 259c Assets:Bids 2006/03/15 Auction House Assets:Tajer 4720c Assets:Bids 2006/03/15 Auction House Expenses:Fees:Auction 12542c ; something got lost here Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 2375c Expenses:Fees:Auction -2375c Assets:Gruulmorg 2006/03/15 Auction House Assets:Danell 4c Equity:Gold 2006/03/15 Transfer Assets:Gruulmorg 2c Assets:Danell 2006/03/15 Transfer Assets:Danell 4250c Expenses:Fees:Auction 750c Assets:Gruulmorg -50s 2006/03/15 Post Expenses:Fees:Mail 60c Assets:Danell 2006/03/15 Player Assets:Tajer 3G Equity:Gold 2006/03/15 Post Assets:Wyshona 40s Expenses:Fees:Mail 30c Assets:Danell 2006/03/15 Auction House Expenses:Fees:Auction 8s Expenses:Fees:Auction 11s Assets:Wyshona 2006/03/15 Auction House Assets:Tajer:Items "Beaststalker's Belt" 1 @ 65G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Vendor Assets:Tajer 16744c Assets:Tajer 16640c Equity:Gold 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 772c Expenses:Fees:Auction 544c Expenses:Fees:Auction 444c Expenses:Fees:Auction 432c Expenses:Fees:Auction 204c Assets:Tajer 2006/03/15 Player Assets:Tajer 12s Equity:Gold 2006/03/15 Vendor Assets:Tajer 22s Equity:Gold 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 1G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 21050c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 23000c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 150s Assets:Tajer 2006/03/16 Player Assets:Tajer 3G Equity:Gold 2006/03/16 Post Expenses:Fees:Mail 90c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 1195768c Assets:Tajer:Items "Beaststalker's Belt" -1 {65G} @ 1195768c Income:Brokering -545768c 2006/03/16 Auction House Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {21050c} Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {2.3G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1.5G} Assets:Tajer:Items 2006/03/16 Player Assets:Tajer 4G Equity:Gold 2006/03/16 Auction House Assets:Wyshona 1341s Equity:Gold 2006/03/16 Auction House Assets:Gruulmorg 4c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 4c Expenses:Fees:Mail 120c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 24s Expenses:Fees:Auction 12s Expenses:Fees:Auction 12s Expenses:Fees:Auction 84c Expenses:Fees:Auction 84c Assets:Wyshona 2006/03/16 Crazy Cat Lady Expenses:Items 40s Expenses:Items 40s Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 60c Assets:Danell 5G Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Tajer 20G Assets:Gruulmorg 2006/03/16 Auction House Assets:Tajer:Items "Pulsating Hydra Heart" 1 @ 1G Assets:Tajer 2006/03/16 Auction House Expenses:Fees:Auction 936c Assets:Tajer 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Gruulmorg 30G Assets:Tajer 2006/03/16 Auction House Assets:Gruulmorg:Items "Ace of Warlords" 2 @ 15G Assets:Gruulmorg 2006/03/16 Transfer Assets:Tajer:Items "Ace of Warlords" 2 {15G} Assets:Gruulmorg:Items 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 2104c Equity:Gold 2006/03/16 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/16 Transfer Assets:Danell 6c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Tajer 2006/03/16 General Goods Vendor Expenses:Items 50c ; wrapping paper Assets:Tajer 2006/03/16 Player Assets:Tajer 1G Equity:Gold 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1.5G} @ 180584c Income:Brokering -165584c 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1G} @ 180584c Income:Brokering -170584c 2006/03/17 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Player: raev Assets:Tajer:Items "Wildheart Belt" 1 @ 30G Assets:Tajer:Items "Ace of Warlords" -2 @ 15G 2006/03/17 Auction House Expenses:Fees:Auction 7482c Assets:Tajer 2006/03/17 Post Expenses:Fees:Mail 300c Assets:Wyshona 2006/03/17 Player Assets:Wyshona 1G Assets:Wyshona:Items "Plans: Wildthorn Mail" -1 {1.25G} @ 1G Expenses:Capital Loss 25s 2006/03/17 Auction House (implicit transfer) Expenses:Items 279s ; Recipe: Swiftness Potion Assets:Wyshona 2006/03/17 Auction House (implicit transfer) Assets:Danell:Items "Ace of Warlords" 1 @ 3G Assets:Tajer:Items "Ace of Warlords" 1 @ 3.9G Assets:Tajer:Items "Holy Bologna" 1 @ 2G Assets:Tajer:Items "The Emerald Dream" 1 @ 4G Assets:Tajer:Items "The Arcanist's Cookbook" 1 @ 4G Assets:Tajer:Items "Harnessing Shadows" 1 @ 5G Assets:Tajer:Items "Garona: Book on Treachery" 1 @ 4G Assets:Tajer:Items "Preserved Holly" 5 @ 20s Assets:Wyshona 2006/03/17 Auction House Assets:Tajer 4G Assets:Tajer:Items "Pulsating Hydra Heart" -1 {1G} @ 4G Income:Brokering -3G 2006/03/17 Auction House Assets:Danell 3171c Assets:Danell:Items "Ace of Warlords" -1 {3G} @ 3171c Expenses:Capital Loss 26829c 2006/03/17 Auction House Expenses:Fees:Auction 12537c Assets:Danell 2006/03/17 Transfer Assets:Gruulmorg 15G Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Auction House Assets:Wyshona 362450c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {21050c} @ 181225c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {2.3G} @ 181225c Income:Brokering -318400c 2006/03/17 Transfer Assets:Danell 499560c Expenses:Gifts 1G Expenses:Fees:Mail 30c Assets:Wyshona 2006/03/17 Post Expenses:Fees:Mail 90c Expenses:Fees:Auction 166c Assets:Gruulmorg 2006/03/17 Transfer Assets:Gruulmorg 459211c Expenses:Fees:Auction 81023c Assets:Danell -540234c 2006/03/17 Transfer Assets:Tajer 662465c Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/17 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Mail 30c Assets:Tajer 2006/03/18 Auction House Assets:Tajer 15G Assets:Tajer:Items "Ace of Warlords" -1 {3.9G} @ 15G Income:Brokering -111000c 2006/03/18 Auction House Assets:Tajer 434472c Assets:Tajer:Items "Wildheart Belt" -1 {30G} @ 434472c Income:Brokering -134472c 2006/03/18 Auction House Assets:Tajer 1995s Assets:Tajer:Items "Harnessing Shadows" -1 {5G} @ 1995s Income:Brokering -1495s 2006/03/19 Auction House Assets:Tajer 2850s Assets:Tajer:Items "Garona: Book on Treachery" -1 {4G} @ 2850s Income:Brokering -2450s 2006/03/19 Auction House Assets:Tajer 1710s Assets:Tajer:Items "The Arcanist's Cookbook" -1 {4G} @ 1710s Income:Brokering -1310s 2006/03/19 Auction House Assets:Tajer 46550c Assets:Tajer:Items "Preserved Holly" -5 {20s} @ 9310c Income:Brokering -36550c 2006/03/19 Auction House Assets:Tajer:Items "Two of Portals" 1 @ 3G Assets:Tajer:Items "Two of Portals" 1 @ 2.5G Assets:Tajer 2006/03/20 Auction House Assets:Tajer 163443c Assets:Tajer:Items "Holy Bologna" -1 {2G} @ 163443c Income:Brokering -143443c 2006/03/20 Auction House Assets:Tajer 5G Assets:Tajer:Items "Two of Portals" -1 {3G} @ 5G Income:Brokering -2G 2006/03/20 Auction House Assets:Tajer 15G Assets:Tajer:Items "The Emerald Dream" -1 {4G} @ 15G Income:Brokering -11G 2006/03/20 Auction House Expenses:Fees:Mail 60c Assets:Tajer 2006/03/21 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 170G Assets:Tajer 2006/03/21 Auction House Expenses:Fees:Auction 2760c Expenses:Fees:Auction 2760c Assets:Tajer 2006/03/22 Auction House Assets:Tajer:Items "Nightblade" 1 @ 200G Assets:Tajer 2006/03/22 Auction House Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/23 Auction House Assets:Tajer 1665260c Assets:Tajer:Items "Orb of Deception" -1 {170G} @ 1665260c Expenses:Capital Loss 34740c 2006/03/26 Auction House Assets:Tajer 81980c Assets:Tajer:Items "Two of Portals" -1 {2.5G} @ 81980c Income:Brokering -56980c 2006/03/26 Player Expenses:Items 150s ; Recipe: Elixir of Minor Agility Expenses:Fees:Mail 30c Expenses:Fees:Mail 30c Assets:Tajer 2006/03/27 Player Assets:Tajer 160G Assets:Tajer:Items "Nightblade" -1 {200G} @ 160G Expenses:Capital Loss 40G 2006/03/27 Player Expenses:Fees:Mail 30c Assets:Tajer 2006/03/26 Player Expenses:Items (9G * 6) ; Traveler's backpacks Expenses:Items 10G Expenses:Fees:Bank 10G Expenses:Fees:Mail 630c Expenses:Fees:Mail 330c Expenses:Fees:Mail 30c Assets:Tajer 2006/04/01 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 155G Assets:Tajer test reg -F '%(justify(scrub(total_expr), 80, 80, true))\n' --lot-prices 133.98G 158.85G 0 14.28s 21.96s 28.08s 75.72s 1.09G 1.22G 1.36G 1.42G 2.43G 2.79G 2.95G 3.79G 4.79G 5.02G 0 15.89G 0 13.20s 0 1.15G 0 32.16s 0 30c 0 3.47G 0 23.16s 0 1.00G 0 5.94G 0 1.20s 0 3.00G 0 6.00G 0 35.00s 0 "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.22G "Plans: Wildthorn Mail" 1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} -77.41s "Plans: Wildthorn Mail" 1 {1.25G} -30.21s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.24G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} 6.75G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.24G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} 6.75G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 2 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} -80.00s "Plans: Wildthorn Mail" 1 {1.25G} -77.41s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} -77.80s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} 42c -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.01G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} -82.50s "Plans: Wildthorn Mail" 1 {1.25G} -75.00s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.24G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} 1.75G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} -85.00s "Plans: Wildthorn Mail" 1 {1.25G} -84.70s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} -1.17G "Plans: Wildthorn Mail" 1 {1.25G} -1.06G "Plans: Wildthorn Mail" 1 {1.25G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -1.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -65.42G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -64.58G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -62.91G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.17G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.12G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.07G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.03G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.01G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.13G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.03G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Beaststalker's Belt" 1 {65.00G} -66.25G "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -67.25G "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -67.25G "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -67.25G "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -66.42G "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -67.25G "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -67.25G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -69.36G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -69.36G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -71.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Beaststalker's Belt" 1 {65.00G} -71.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Beaststalker's Belt" 1 {65.00G} -73.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Beaststalker's Belt" 1 {65.00G} -70.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Beaststalker's Belt" 1 {65.00G} -73.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Beaststalker's Belt" 1 {65.00G} -73.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Beaststalker's Belt" 1 {65.00G} -73.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Beaststalker's Belt" 1 {65.00G} 46.42G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} 46.42G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 2 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 2 {2.105G} "Plans: Mithril Shield Spike" 2 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 2 {2.105G} "Plans: Mithril Shield Spike" 2 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 2 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 2 {2.105G} "Plans: Mithril Shield Spike" 2 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 2 {1.00G} "Recipe: Elixir of Giant Growth" 2 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 2 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 2 {1.00G} "Recipe: Elixir of Giant Growth" 2 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 2 {1.00G} "Recipe: Elixir of Giant Growth" 2 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 2 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -4.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} 5.25G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.14G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -7.92G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -7.80G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -7.68G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -7.67G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -7.66G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -7.75G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -7.36G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -3.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} 11.85G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -9.06G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} 20.85G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 4 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.14G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -38.94G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -38.40G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -37.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -38.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -21.10G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.50G} "Ace of Warlords" 2 {15.00G} -21.10G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Ace of Warlords" 2 {15.00G} -37.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Ace of Warlords" 2 {15.00G} -19.60G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} "Ace of Warlords" 2 {15.00G} -19.60G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Ace of Warlords" 2 {15.00G} -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Ace of Warlords" 2 {15.00G} -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Ace of Warlords" 2 {15.00G} -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Ace of Warlords" 2 {15.00G} -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -35.91G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -36.62G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -35.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Wildthorn Mail" 1 {1.25G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -35.65G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -35.40G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -32.62G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} -35.40G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} -35.40G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -35.40G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -35.40G "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -35.40G "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -35.40G "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -35.40G "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -35.40G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Pulsating Hydra Heart" 1 {1.00G} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -35.40G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "Pulsating Hydra Heart" 1 {1.00G} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -62.31G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "Pulsating Hydra Heart" 1 {1.00G} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -58.31G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "Pulsating Hydra Heart" 1 {1.00G} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -58.31G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -61.31G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.00G} "Ace of Warlords" 1 {3.90G} -60.99G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -60.99G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -58.31G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -57.05G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -58.31G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -43.31G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -43.30G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -58.31G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -22.06G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -22.06G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Plans: Mithril Shield Spike" 1 {2.30G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -22.06G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -53.90G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -3.94G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -2.94G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -2.94G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -53.90G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -53.89G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -53.87G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -53.90G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -7.98G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} 12.34s "Ace of Warlords" 1 {3.90G} -53.90G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} 12.35G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} 12.35G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -53.90G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -53.15G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -53.15G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -53.90G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} "Ace of Warlords" 1 {3.90G} -38.90G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} -38.90G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} -50.00G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} -6.55G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Wildheart Belt" 1 {30.00G} -6.55G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} -20.00G "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} "Harnessing Shadows" 1 {5.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} -5.00s "Garona: Book on Treachery" 1 {4.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} -5.00s -15.00G "Garona: Book on Treachery" 1 {4.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} 13.50G "Garona: Book on Treachery" 1 {4.00G} "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} 13.50G "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} -11.00G "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} 6.10G "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Arcanist's Cookbook" 1 {4.00G} "The Emerald Dream" 1 {4.00G} 6.10G "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Emerald Dream" 1 {4.00G} -7.00G "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Emerald Dream" 1 {4.00G} -2.35G "Holy Bologna" 1 {2.00G} "Preserved Holly" 5 {20.00s} "The Emerald Dream" 1 {4.00G} -2.35G "Holy Bologna" 1 {2.00G} "The Emerald Dream" 1 {4.00G} -6.00G "Holy Bologna" 1 {2.00G} "The Emerald Dream" 1 {4.00G} -6.00G "Holy Bologna" 1 {2.00G} "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {3.00G} -6.00G "Holy Bologna" 1 {2.00G} "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {2.50G} "Two of Portals" 1 {3.00G} -11.50G "Holy Bologna" 1 {2.00G} "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {2.50G} "Two of Portals" 1 {3.00G} 4.84G "Holy Bologna" 1 {2.00G} "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {2.50G} "Two of Portals" 1 {3.00G} 4.84G "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {2.50G} "Two of Portals" 1 {3.00G} -9.50G "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {2.50G} "Two of Portals" 1 {3.00G} -4.50G "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {2.50G} "Two of Portals" 1 {3.00G} -4.50G "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {2.50G} -6.50G "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {2.50G} 8.50G "The Emerald Dream" 1 {4.00G} "Two of Portals" 1 {2.50G} 8.50G "Two of Portals" 1 {2.50G} -2.50G "Two of Portals" 1 {2.50G} -2.49G "Two of Portals" 1 {2.50G} -2.50G "Two of Portals" 1 {2.50G} -2.50G "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -172.50G "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -172.22G "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -171.95G "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -172.50G "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -172.50G Nightblade 1 {200.00G} "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -372.50G Nightblade 1 {200.00G} "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -370.73G Nightblade 1 {200.00G} "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -368.96G Nightblade 1 {200.00G} "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -367.19G Nightblade 1 {200.00G} "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -365.42G Nightblade 1 {200.00G} "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -364.67G Nightblade 1 {200.00G} "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -372.50G Nightblade 1 {200.00G} "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -205.97G Nightblade 1 {200.00G} "Orb of Deception" 1 {170.00G} "Two of Portals" 1 {2.50G} -205.97G Nightblade 1 {200.00G} "Two of Portals" 1 {2.50G} -202.50G Nightblade 1 {200.00G} "Two of Portals" 1 {2.50G} -194.30G Nightblade 1 {200.00G} "Two of Portals" 1 {2.50G} -194.30G Nightblade 1 {200.00G} -200.00G Nightblade 1 {200.00G} -198.50G Nightblade 1 {200.00G} -198.50G Nightblade 1 {200.00G} -198.49G Nightblade 1 {200.00G} -200.00G Nightblade 1 {200.00G} -40.00G Nightblade 1 {200.00G} -40.00G 0 30c 0 54.00G 64.00G 74.00G 74.06G 74.10G 74.10G 0 "Orb of Deception" 1 {155.00G} -155.00G "Orb of Deception" 1 {155.00G} end test ��������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-lot-tags.test��������������������������������������������������������0000664�0000000�0000000�00000000324�14411236400�0020747�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1.00c 2006/03/14 Opening Balances Assets:Tajer 1339829c (TAG) Assets:Gruulmorg 248720c Equity:Gold test bal --lot-tags tajer 1339829.00c (TAG) Assets:Tajer end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-lots-actual.test�����������������������������������������������������0000664�0000000�0000000�00000001441�14411236400�0021446�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1.0000s 2006/03/14 Opening Balances Assets:Tajer 1339829c @ 1.86590975416s Assets:Gruulmorg 248720c @ 10.051463493s Equity:Gold -5000000s test reg --format '%(justify(scrub(total_expr), 40, 40, true))\n' --lots --date-format %Y/%m/%d 1339829c {1.86590975416s} [2006/03/14] 1339829c {1.86590975416s} [2006/03/14] 248720c {10.051463493s} [2006/03/14] 1339829c {1.86590975416s} [2006/03/14] 248720c {10.051463493s} [2006/03/14] -1388.89h end test test reg --format '%(justify(scrub(total_expr), 40, 40, true))\n' --lots-actual 1339829c 1588549c 1588549c -1388.89h end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-lots.test������������������������������������������������������������0000664�0000000�0000000�00001404074�14411236400�0020211�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������C 1.00s = 100c C 1.00G = 100s D 1.00G 2006/03/14 Opening Balances Assets:Tajer 1339829c Assets:Gruulmorg 248720c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1428c Expenses:Fees:Auction 768c Expenses:Fees:Auction 612c Expenses:Fees:Auction 4764c Expenses:Fees:Auction 3372c Expenses:Fees:Auction 1296c Expenses:Fees:Auction 1332c Expenses:Fees:Auction 660c Expenses:Fees:Auction 10044c Expenses:Fees:Auction 3588c Expenses:Fees:Auction 1632c Expenses:Fees:Auction 8388c Expenses:Fees:Auction 9984c Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 158860c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1320c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 11496c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 3216c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/14 Auction House Assets:Tajer 34678c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Gruulmorg 1G Equity:Gold 2006/03/14 Auction House Assets:Tajer 59389c Equity:Gold 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Player Assets:Tajer 3G Equity:Gold 2006/03/14 Player Assets:Tajer 6G Equity:Gold 2006/03/14 Auction House Expenses:Items 35s Assets:Tajer 2006/03/14 Auction House Assets:Tajer:Items "Plans: Wildthorn Mail" 1 @ 125s Assets:Tajer 2006/03/14 Auction House Assets:Bids 259c Assets:Bids 45s Assets:Bids 4720c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Auction House Expenses:Items 8G Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Puldoost Assets:Tajer 8G Expenses:Items 2006/03/14 Auction House Assets:Wyshona:Items "Plans: Wildthorn Mail" 1 {1.25G} Assets:Tajer:Items 2006/03/15 Auction House Assets:Tajer 45s Assets:Tajer 259c Assets:Bids 2006/03/15 Auction House Assets:Tajer 4720c Assets:Bids 2006/03/15 Auction House Expenses:Fees:Auction 12542c ; something got lost here Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 2375c Expenses:Fees:Auction -2375c Assets:Gruulmorg 2006/03/15 Auction House Assets:Danell 4c Equity:Gold 2006/03/15 Transfer Assets:Gruulmorg 2c Assets:Danell 2006/03/15 Transfer Assets:Danell 4250c Expenses:Fees:Auction 750c Assets:Gruulmorg -50s 2006/03/15 Post Expenses:Fees:Mail 60c Assets:Danell 2006/03/15 Player Assets:Tajer 3G Equity:Gold 2006/03/15 Post Assets:Wyshona 40s Expenses:Fees:Mail 30c Assets:Danell 2006/03/15 Auction House Expenses:Fees:Auction 8s Expenses:Fees:Auction 11s Assets:Wyshona 2006/03/15 Auction House Assets:Tajer:Items "Beaststalker's Belt" 1 @ 65G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Vendor Assets:Tajer 16744c Assets:Tajer 16640c Equity:Gold 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 772c Expenses:Fees:Auction 544c Expenses:Fees:Auction 444c Expenses:Fees:Auction 432c Expenses:Fees:Auction 204c Assets:Tajer 2006/03/15 Player Assets:Tajer 12s Equity:Gold 2006/03/15 Vendor Assets:Tajer 22s Equity:Gold 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 1G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 21050c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 23000c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 150s Assets:Tajer 2006/03/16 Player Assets:Tajer 3G Equity:Gold 2006/03/16 Post Expenses:Fees:Mail 90c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 1195768c Assets:Tajer:Items "Beaststalker's Belt" -1 {65G} @ 1195768c Income:Brokering -545768c 2006/03/16 Auction House Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {21050c} Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {2.3G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1.5G} Assets:Tajer:Items 2006/03/16 Player Assets:Tajer 4G Equity:Gold 2006/03/16 Auction House Assets:Wyshona 1341s Equity:Gold 2006/03/16 Auction House Assets:Gruulmorg 4c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 4c Expenses:Fees:Mail 120c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 24s Expenses:Fees:Auction 12s Expenses:Fees:Auction 12s Expenses:Fees:Auction 84c Expenses:Fees:Auction 84c Assets:Wyshona 2006/03/16 Crazy Cat Lady Expenses:Items 40s Expenses:Items 40s Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 60c Assets:Danell 5G Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Tajer 20G Assets:Gruulmorg 2006/03/16 Auction House Assets:Tajer:Items "Pulsating Hydra Heart" 1 @ 1G Assets:Tajer 2006/03/16 Auction House Expenses:Fees:Auction 936c Assets:Tajer 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Gruulmorg 30G Assets:Tajer 2006/03/16 Auction House Assets:Gruulmorg:Items "Ace of Warlords" 2 @ 15G Assets:Gruulmorg 2006/03/16 Transfer Assets:Tajer:Items "Ace of Warlords" 2 {15G} Assets:Gruulmorg:Items 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 2104c Equity:Gold 2006/03/16 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/16 Transfer Assets:Danell 6c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Tajer 2006/03/16 General Goods Vendor Expenses:Items 50c ; wrapping paper Assets:Tajer 2006/03/16 Player Assets:Tajer 1G Equity:Gold 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1.5G} @ 180584c Income:Brokering -165584c 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1G} @ 180584c Income:Brokering -170584c 2006/03/17 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Player: raev Assets:Tajer:Items "Wildheart Belt" 1 @ 30G Assets:Tajer:Items "Ace of Warlords" -2 @ 15G 2006/03/17 Auction House Expenses:Fees:Auction 7482c Assets:Tajer 2006/03/17 Post Expenses:Fees:Mail 300c Assets:Wyshona 2006/03/17 Player Assets:Wyshona 1G Assets:Wyshona:Items "Plans: Wildthorn Mail" -1 {1.25G} @ 1G Expenses:Capital Loss 25s 2006/03/17 Auction House (implicit transfer) Expenses:Items 279s ; Recipe: Swiftness Potion Assets:Wyshona 2006/03/17 Auction House (implicit transfer) Assets:Danell:Items "Ace of Warlords" 1 @ 3G Assets:Tajer:Items "Ace of Warlords" 1 @ 3.9G Assets:Tajer:Items "Holy Bologna" 1 @ 2G Assets:Tajer:Items "The Emerald Dream" 1 @ 4G Assets:Tajer:Items "The Arcanist's Cookbook" 1 @ 4G Assets:Tajer:Items "Harnessing Shadows" 1 @ 5G Assets:Tajer:Items "Garona: Book on Treachery" 1 @ 4G Assets:Tajer:Items "Preserved Holly" 5 @ 20s Assets:Wyshona 2006/03/17 Auction House Assets:Tajer 4G Assets:Tajer:Items "Pulsating Hydra Heart" -1 {1G} @ 4G Income:Brokering -3G 2006/03/17 Auction House Assets:Danell 3171c Assets:Danell:Items "Ace of Warlords" -1 {3G} @ 3171c Expenses:Capital Loss 26829c 2006/03/17 Auction House Expenses:Fees:Auction 12537c Assets:Danell 2006/03/17 Transfer Assets:Gruulmorg 15G Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Auction House Assets:Wyshona 362450c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {21050c} @ 181225c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {2.3G} @ 181225c Income:Brokering -318400c 2006/03/17 Transfer Assets:Danell 499560c Expenses:Gifts 1G Expenses:Fees:Mail 30c Assets:Wyshona 2006/03/17 Post Expenses:Fees:Mail 90c Expenses:Fees:Auction 166c Assets:Gruulmorg 2006/03/17 Transfer Assets:Gruulmorg 459211c Expenses:Fees:Auction 81023c Assets:Danell -540234c 2006/03/17 Transfer Assets:Tajer 662465c Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/17 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Mail 30c Assets:Tajer 2006/03/18 Auction House Assets:Tajer 15G Assets:Tajer:Items "Ace of Warlords" -1 {3.9G} @ 15G Income:Brokering -111000c 2006/03/18 Auction House Assets:Tajer 434472c Assets:Tajer:Items "Wildheart Belt" -1 {30G} @ 434472c Income:Brokering -134472c 2006/03/18 Auction House Assets:Tajer 1995s Assets:Tajer:Items "Harnessing Shadows" -1 {5G} @ 1995s Income:Brokering -1495s 2006/03/19 Auction House Assets:Tajer 2850s Assets:Tajer:Items "Garona: Book on Treachery" -1 {4G} @ 2850s Income:Brokering -2450s 2006/03/19 Auction House Assets:Tajer 1710s Assets:Tajer:Items "The Arcanist's Cookbook" -1 {4G} @ 1710s Income:Brokering -1310s 2006/03/19 Auction House Assets:Tajer 46550c Assets:Tajer:Items "Preserved Holly" -5 {20s} @ 9310c Income:Brokering -36550c 2006/03/19 Auction House Assets:Tajer:Items "Two of Portals" 1 @ 3G Assets:Tajer:Items "Two of Portals" 1 @ 2.5G Assets:Tajer 2006/03/20 Auction House Assets:Tajer 163443c Assets:Tajer:Items "Holy Bologna" -1 {2G} @ 163443c Income:Brokering -143443c 2006/03/20 Auction House Assets:Tajer 5G Assets:Tajer:Items "Two of Portals" -1 {3G} @ 5G Income:Brokering -2G 2006/03/20 Auction House Assets:Tajer 15G Assets:Tajer:Items "The Emerald Dream" -1 {4G} @ 15G Income:Brokering -11G 2006/03/20 Auction House Expenses:Fees:Mail 60c Assets:Tajer 2006/03/21 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 170G Assets:Tajer 2006/03/21 Auction House Expenses:Fees:Auction 2760c Expenses:Fees:Auction 2760c Assets:Tajer 2006/03/22 Auction House Assets:Tajer:Items "Nightblade" 1 @ 200G Assets:Tajer 2006/03/22 Auction House Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/23 Auction House Assets:Tajer 1665260c Assets:Tajer:Items "Orb of Deception" -1 {170G} @ 1665260c Expenses:Capital Loss 34740c 2006/03/26 Auction House Assets:Tajer 81980c Assets:Tajer:Items "Two of Portals" -1 {2.5G} @ 81980c Income:Brokering -56980c 2006/03/26 Player Expenses:Items 150s ; Recipe: Elixir of Minor Agility Expenses:Fees:Mail 30c Expenses:Fees:Mail 30c Assets:Tajer 2006/03/27 Player Assets:Tajer 160G Assets:Tajer:Items "Nightblade" -1 {200G} @ 160G Expenses:Capital Loss 40G 2006/03/27 Player Expenses:Fees:Mail 30c Assets:Tajer 2006/03/26 Player Expenses:Items (9G * 6) ; Traveler's backpacks Expenses:Items 10G Expenses:Fees:Bank 10G Expenses:Fees:Mail 630c Expenses:Fees:Mail 330c Expenses:Fees:Mail 30c Assets:Tajer 2006/04/01 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 155G Assets:Tajer test reg -F '%(justify(scrub(total_expr), 80, 80, true))\n' --lots --date-format %Y/%m/%d 133.98G 158.85G 0 14.28s 21.96s 28.08s 75.72s 1.09G 1.22G 1.36G 1.42G 2.43G 2.79G 2.95G 3.79G 4.79G 5.02G 0 15.89G 0 13.20s 0 1.15G 0 32.16s 0 30c 0 3.47G 0 23.16s 0 1.00G 0 5.94G 0 1.20s 0 3.00G 0 6.00G 0 35.00s 0 "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.22G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -77.41s "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -30.21s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.24G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] 6.75G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.24G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] 6.75G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -80.00s "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -77.41s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -77.80s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] 42c -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.01G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -82.50s "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -75.00s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.24G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] 1.75G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -85.00s "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -84.70s -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.17G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.06G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -1.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -65.42G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -64.58G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -62.91G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.17G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.12G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.07G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.03G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.01G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.13G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.03G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -67.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -67.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -67.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -66.42G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -67.25G "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -67.25G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -69.36G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -69.36G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -71.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -71.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -73.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -70.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -73.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -73.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -73.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 46.42G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 46.42G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -4.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 5.25G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.14G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -7.92G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -7.80G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -7.68G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -7.67G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -7.66G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -7.75G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -7.36G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -3.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 11.85G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -8.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -9.06G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 20.85G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -9.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.14G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -38.94G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -38.40G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -37.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -38.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -39.15G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -21.10G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -21.10G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -37.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -19.60G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -19.60G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.91G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -36.62G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -36.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.65G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -32.62G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -35.40G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -62.31G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -58.31G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -58.31G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -61.31G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -60.99G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -60.99G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -58.31G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -57.05G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -58.31G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -43.31G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -43.30G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -58.31G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -22.06G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -22.06G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -22.06G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.90G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -3.94G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -2.94G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -2.94G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.90G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.89G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.87G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.90G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -7.98G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] 12.34s "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.90G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 12.35G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 12.35G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.90G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.15G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.15G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -53.90G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -38.90G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -38.90G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -50.00G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -6.55G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -6.55G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -20.00G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] -5.00s "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] -5.00s "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -15.00G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 13.50G "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 13.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -11.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 6.10G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 6.10G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -7.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -2.35G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -2.35G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -6.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -6.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -6.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -11.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 4.84G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 4.84G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -9.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -4.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -4.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -6.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 8.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 8.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -2.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -2.49G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -2.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -2.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -172.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -172.22G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -171.95G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -172.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -172.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -372.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -370.73G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -368.96G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -367.19G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -365.42G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -364.67G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -372.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -205.97G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -205.97G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -202.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -194.30G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -194.30G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -200.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -198.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -198.50G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -198.49G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -200.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -40.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -40.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] 30c "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 54.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 64.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 74.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 74.06G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 74.10G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] 74.10G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {155.00G} [2006/04/01] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] "Ace of Warlords" -1 {3.00G} "Ace of Warlords" 1 {3.00G} [2006/03/17] "Ace of Warlords" -1 {3.90G} "Ace of Warlords" 1 {3.90G} [2006/03/17] "Ace of Warlords" 2 {15.00G} [2006/03/16] "Ace of Warlords" -2 {15.00G} [2006/03/17] "Beaststalker's Belt" -1 {65.00G} "Beaststalker's Belt" 1 {65.00G} [2006/03/15] -155.00G "Garona: Book on Treachery" -1 {4.00G} "Garona: Book on Treachery" 1 {4.00G} [2006/03/17] "Harnessing Shadows" -1 {5.00G} "Harnessing Shadows" 1 {5.00G} [2006/03/17] "Holy Bologna" -1 {2.00G} "Holy Bologna" 1 {2.00G} [2006/03/17] Nightblade -1 {200.00G} Nightblade 1 {200.00G} [2006/03/22] "Orb of Deception" 1 {155.00G} [2006/04/01] "Orb of Deception" -1 {170.00G} "Orb of Deception" 1 {170.00G} [2006/03/21] "Plans: Mithril Shield Spike" -1 {2.105G} "Plans: Mithril Shield Spike" 1 {2.105G} [2006/03/15] "Plans: Mithril Shield Spike" -1 {2.30G} "Plans: Mithril Shield Spike" 1 {2.30G} [2006/03/15] "Plans: Wildthorn Mail" -1 {1.25G} "Plans: Wildthorn Mail" 1 {1.25G} [2006/03/14] "Preserved Holly" -5 {20.00s} "Preserved Holly" 5 {20.00s} [2006/03/17] "Pulsating Hydra Heart" -1 {1.00G} "Pulsating Hydra Heart" 1 {1.00G} [2006/03/16] "Recipe: Elixir of Giant Growth" -1 {1.00G} "Recipe: Elixir of Giant Growth" 1 {1.00G} [2006/03/15] "Recipe: Elixir of Giant Growth" -1 {1.50G} "Recipe: Elixir of Giant Growth" 1 {1.50G} [2006/03/15] "The Arcanist's Cookbook" -1 {4.00G} "The Arcanist's Cookbook" 1 {4.00G} [2006/03/17] "The Emerald Dream" -1 {4.00G} "The Emerald Dream" 1 {4.00G} [2006/03/17] "Two of Portals" -1 {2.50G} "Two of Portals" 1 {2.50G} [2006/03/19] "Two of Portals" -1 {3.00G} "Two of Portals" 1 {3.00G} [2006/03/19] "Wildheart Belt" -1 {30.00G} "Wildheart Belt" 1 {30.00G} [2006/03/17] end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-lots_basis.test������������������������������������������������������0000664�0000000�0000000�00000121403�14411236400�0021361�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������C 1.00s = 100c C 1.00G = 100s D 1.00G 2006/03/14 Opening Balances Assets:Tajer 1339829c Assets:Gruulmorg 248720c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1428c Expenses:Fees:Auction 768c Expenses:Fees:Auction 612c Expenses:Fees:Auction 4764c Expenses:Fees:Auction 3372c Expenses:Fees:Auction 1296c Expenses:Fees:Auction 1332c Expenses:Fees:Auction 660c Expenses:Fees:Auction 10044c Expenses:Fees:Auction 3588c Expenses:Fees:Auction 1632c Expenses:Fees:Auction 8388c Expenses:Fees:Auction 9984c Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 158860c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1320c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 11496c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 3216c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/14 Auction House Assets:Tajer 34678c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Gruulmorg 1G Equity:Gold 2006/03/14 Auction House Assets:Tajer 59389c Equity:Gold 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Player Assets:Tajer 3G Equity:Gold 2006/03/14 Player Assets:Tajer 6G Equity:Gold 2006/03/14 Auction House Expenses:Items 35s Assets:Tajer 2006/03/14 Auction House Assets:Tajer:Items "Plans: Wildthorn Mail" 1 @ 125s Assets:Tajer 2006/03/14 Auction House Assets:Bids 259c Assets:Bids 45s Assets:Bids 4720c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Auction House Expenses:Items 8G Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Puldoost Assets:Tajer 8G Expenses:Items 2006/03/14 Auction House Assets:Wyshona:Items "Plans: Wildthorn Mail" 1 {1.25G} Assets:Tajer:Items 2006/03/15 Auction House Assets:Tajer 45s Assets:Tajer 259c Assets:Bids 2006/03/15 Auction House Assets:Tajer 4720c Assets:Bids 2006/03/15 Auction House Expenses:Fees:Auction 12542c ; something got lost here Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 2375c Expenses:Fees:Auction -2375c Assets:Gruulmorg 2006/03/15 Auction House Assets:Danell 4c Equity:Gold 2006/03/15 Transfer Assets:Gruulmorg 2c Assets:Danell 2006/03/15 Transfer Assets:Danell 4250c Expenses:Fees:Auction 750c Assets:Gruulmorg -50s 2006/03/15 Post Expenses:Fees:Mail 60c Assets:Danell 2006/03/15 Player Assets:Tajer 3G Equity:Gold 2006/03/15 Post Assets:Wyshona 40s Expenses:Fees:Mail 30c Assets:Danell 2006/03/15 Auction House Expenses:Fees:Auction 8s Expenses:Fees:Auction 11s Assets:Wyshona 2006/03/15 Auction House Assets:Tajer:Items "Beaststalker's Belt" 1 @ 65G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Vendor Assets:Tajer 16744c Assets:Tajer 16640c Equity:Gold 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 772c Expenses:Fees:Auction 544c Expenses:Fees:Auction 444c Expenses:Fees:Auction 432c Expenses:Fees:Auction 204c Assets:Tajer 2006/03/15 Player Assets:Tajer 12s Equity:Gold 2006/03/15 Vendor Assets:Tajer 22s Equity:Gold 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 1G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 21050c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 23000c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 150s Assets:Tajer 2006/03/16 Player Assets:Tajer 3G Equity:Gold 2006/03/16 Post Expenses:Fees:Mail 90c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 1195768c Assets:Tajer:Items "Beaststalker's Belt" -1 {65G} @ 1195768c Income:Brokering -545768c 2006/03/16 Auction House Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {21050c} Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {2.3G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1.5G} Assets:Tajer:Items 2006/03/16 Player Assets:Tajer 4G Equity:Gold 2006/03/16 Auction House Assets:Wyshona 1341s Equity:Gold 2006/03/16 Auction House Assets:Gruulmorg 4c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 4c Expenses:Fees:Mail 120c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 24s Expenses:Fees:Auction 12s Expenses:Fees:Auction 12s Expenses:Fees:Auction 84c Expenses:Fees:Auction 84c Assets:Wyshona 2006/03/16 Crazy Cat Lady Expenses:Items 40s Expenses:Items 40s Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 60c Assets:Danell 5G Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Tajer 20G Assets:Gruulmorg 2006/03/16 Auction House Assets:Tajer:Items "Pulsating Hydra Heart" 1 @ 1G Assets:Tajer 2006/03/16 Auction House Expenses:Fees:Auction 936c Assets:Tajer 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Gruulmorg 30G Assets:Tajer 2006/03/16 Auction House Assets:Gruulmorg:Items "Ace of Warlords" 2 @ 15G Assets:Gruulmorg 2006/03/16 Transfer Assets:Tajer:Items "Ace of Warlords" 2 {15G} Assets:Gruulmorg:Items 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 2104c Equity:Gold 2006/03/16 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/16 Transfer Assets:Danell 6c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Tajer 2006/03/16 General Goods Vendor Expenses:Items 50c ; wrapping paper Assets:Tajer 2006/03/16 Player Assets:Tajer 1G Equity:Gold 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1.5G} @ 180584c Income:Brokering -165584c 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1G} @ 180584c Income:Brokering -170584c 2006/03/17 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Player: raev Assets:Tajer:Items "Wildheart Belt" 1 @ 30G Assets:Tajer:Items "Ace of Warlords" -2 @ 15G 2006/03/17 Auction House Expenses:Fees:Auction 7482c Assets:Tajer 2006/03/17 Post Expenses:Fees:Mail 300c Assets:Wyshona 2006/03/17 Player Assets:Wyshona 1G Assets:Wyshona:Items "Plans: Wildthorn Mail" -1 {1.25G} @ 1G Expenses:Capital Loss 25s 2006/03/17 Auction House (implicit transfer) Expenses:Items 279s ; Recipe: Swiftness Potion Assets:Wyshona 2006/03/17 Auction House (implicit transfer) Assets:Danell:Items "Ace of Warlords" 1 @ 3G Assets:Tajer:Items "Ace of Warlords" 1 @ 3.9G Assets:Tajer:Items "Holy Bologna" 1 @ 2G Assets:Tajer:Items "The Emerald Dream" 1 @ 4G Assets:Tajer:Items "The Arcanist's Cookbook" 1 @ 4G Assets:Tajer:Items "Harnessing Shadows" 1 @ 5G Assets:Tajer:Items "Garona: Book on Treachery" 1 @ 4G Assets:Tajer:Items "Preserved Holly" 5 @ 20s Assets:Wyshona 2006/03/17 Auction House Assets:Tajer 4G Assets:Tajer:Items "Pulsating Hydra Heart" -1 {1G} @ 4G Income:Brokering -3G 2006/03/17 Auction House Assets:Danell 3171c Assets:Danell:Items "Ace of Warlords" -1 {3G} @ 3171c Expenses:Capital Loss 26829c 2006/03/17 Auction House Expenses:Fees:Auction 12537c Assets:Danell 2006/03/17 Transfer Assets:Gruulmorg 15G Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Auction House Assets:Wyshona 362450c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {21050c} @ 181225c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {2.3G} @ 181225c Income:Brokering -318400c 2006/03/17 Transfer Assets:Danell 499560c Expenses:Gifts 1G Expenses:Fees:Mail 30c Assets:Wyshona 2006/03/17 Post Expenses:Fees:Mail 90c Expenses:Fees:Auction 166c Assets:Gruulmorg 2006/03/17 Transfer Assets:Gruulmorg 459211c Expenses:Fees:Auction 81023c Assets:Danell -540234c 2006/03/17 Transfer Assets:Tajer 662465c Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/17 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Mail 30c Assets:Tajer 2006/03/18 Auction House Assets:Tajer 15G Assets:Tajer:Items "Ace of Warlords" -1 {3.9G} @ 15G Income:Brokering -111000c 2006/03/18 Auction House Assets:Tajer 434472c Assets:Tajer:Items "Wildheart Belt" -1 {30G} @ 434472c Income:Brokering -134472c 2006/03/18 Auction House Assets:Tajer 1995s Assets:Tajer:Items "Harnessing Shadows" -1 {5G} @ 1995s Income:Brokering -1495s 2006/03/19 Auction House Assets:Tajer 2850s Assets:Tajer:Items "Garona: Book on Treachery" -1 {4G} @ 2850s Income:Brokering -2450s 2006/03/19 Auction House Assets:Tajer 1710s Assets:Tajer:Items "The Arcanist's Cookbook" -1 {4G} @ 1710s Income:Brokering -1310s 2006/03/19 Auction House Assets:Tajer 46550c Assets:Tajer:Items "Preserved Holly" -5 {20s} @ 9310c Income:Brokering -36550c 2006/03/19 Auction House Assets:Tajer:Items "Two of Portals" 1 @ 3G Assets:Tajer:Items "Two of Portals" 1 @ 2.5G Assets:Tajer 2006/03/20 Auction House Assets:Tajer 163443c Assets:Tajer:Items "Holy Bologna" -1 {2G} @ 163443c Income:Brokering -143443c 2006/03/20 Auction House Assets:Tajer 5G Assets:Tajer:Items "Two of Portals" -1 {3G} @ 5G Income:Brokering -2G 2006/03/20 Auction House Assets:Tajer 15G Assets:Tajer:Items "The Emerald Dream" -1 {4G} @ 15G Income:Brokering -11G 2006/03/20 Auction House Expenses:Fees:Mail 60c Assets:Tajer 2006/03/21 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 170G Assets:Tajer 2006/03/21 Auction House Expenses:Fees:Auction 2760c Expenses:Fees:Auction 2760c Assets:Tajer 2006/03/22 Auction House Assets:Tajer:Items "Nightblade" 1 @ 200G Assets:Tajer 2006/03/22 Auction House Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/23 Auction House Assets:Tajer 1665260c Assets:Tajer:Items "Orb of Deception" -1 {170G} @ 1665260c Expenses:Capital Loss 34740c 2006/03/26 Auction House Assets:Tajer 81980c Assets:Tajer:Items "Two of Portals" -1 {2.5G} @ 81980c Income:Brokering -56980c 2006/03/26 Player Expenses:Items 150s ; Recipe: Elixir of Minor Agility Expenses:Fees:Mail 30c Expenses:Fees:Mail 30c Assets:Tajer 2006/03/27 Player Assets:Tajer 160G Assets:Tajer:Items "Nightblade" -1 {200G} @ 160G Expenses:Capital Loss 40G 2006/03/27 Player Expenses:Fees:Mail 30c Assets:Tajer 2006/03/26 Player Expenses:Items (9G * 6) ; Traveler's backpacks Expenses:Items 10G Expenses:Fees:Bank 10G Expenses:Fees:Mail 630c Expenses:Fees:Mail 330c Expenses:Fees:Mail 30c Assets:Tajer 2006/04/01 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 155G Assets:Tajer test reg --basis 06-Mar-14 Opening Balances Assets:Tajer 133.98G 133.98G Assets:Gruulmorg 24.87G 158.85G Equity:Gold -158.85G 0 06-Mar-14 Auction House Expenses:Fees:Auction 14.28s 14.28s Expenses:Fees:Auction 7.68s 21.96s Expenses:Fees:Auction 6.12s 28.08s Expenses:Fees:Auction 47.64s 75.72s Expenses:Fees:Auction 33.72s 1.09G Expenses:Fees:Auction 12.96s 1.22G Expenses:Fees:Auction 13.32s 1.36G Expenses:Fees:Auction 6.60s 1.42G Expenses:Fees:Auction 1.00G 2.43G Expenses:Fees:Auction 35.88s 2.79G Expenses:Fees:Auction 16.32s 2.95G Expenses:Fees:Auction 83.88s 3.79G Expenses:Fees:Auction 99.84s 4.79G Expenses:Fees:Auction 23.16s 5.02G Assets:Tajer -5.02G 0 06-Mar-14 Auction House Assets:Tajer 15.89G 15.89G Equity:Gold -15.89G 0 06-Mar-14 Auction House Expenses:Fees:Auction 13.20s 13.20s Assets:Tajer -13.20s 0 06-Mar-14 Auction House Assets:Tajer 1.15G 1.15G Equity:Gold -1.15G 0 06-Mar-14 Auction House Expenses:Fees:Auction 32.16s 32.16s Assets:Tajer -32.16s 0 06-Mar-14 Post Expenses:Fees:Mail 30c 30c Assets:Gruulmorg -30c 0 06-Mar-14 Auction House Assets:Tajer 3.47G 3.47G Equity:Gold -3.47G 0 06-Mar-14 Auction House Expenses:Fees:Auction 23.16s 23.16s Assets:Tajer -23.16s 0 06-Mar-14 Auction House Assets:Gruulmorg 1.00G 1.00G Equity:Gold -1.00G 0 06-Mar-14 Auction House Assets:Tajer 5.94G 5.94G Equity:Gold -5.94G 0 06-Mar-14 Post Expenses:Fees:Mail 1.20s 1.20s Assets:Tajer -1.20s 0 06-Mar-14 Player Assets:Tajer 3.00G 3.00G Equity:Gold -3.00G 0 06-Mar-14 Player Assets:Tajer 6.00G 6.00G Equity:Gold -6.00G 0 06-Mar-14 Auction House Expenses:Items 35.00s 35.00s Assets:Tajer -35.00s 0 06-Mar-14 Auction House Assets:Tajer:Items 1.25G 1.25G Assets:Tajer -1.25G 0 06-Mar-14 Auction House Assets:Bids 2.59s 2.59s Assets:Bids 45.00s 47.59s Assets:Bids 47.20s 94.79s Assets:Tajer -94.79s 0 06-Mar-14 Post Expenses:Fees:Mail 1.20s 1.20s Assets:Tajer -1.20s 0 06-Mar-14 Auction House Expenses:Items 8.00G 8.00G Assets:Tajer -8.00G 0 06-Mar-14 Post Expenses:Fees:Mail 1.20s 1.20s Assets:Tajer -1.20s 0 06-Mar-14 Puldoost Assets:Tajer 8.00G 8.00G Expenses:Items -8.00G 0 06-Mar-14 Auction House Assets:Wyshona:Items "Plans: Wildthorn Mail" 1 "Plans: Wildthorn Mail" 1 Assets:Tajer:Items "Plans: Wildthorn Mail" -1 0 06-Mar-15 Auction House Assets:Tajer 45.00s 45.00s Assets:Tajer 2.59s 47.59s Assets:Bids -47.59s 0 06-Mar-15 Auction House Assets:Tajer 47.20s 47.20s Assets:Bids -47.20s 0 06-Mar-15 Auction House Expenses:Fees:Auction 1.25G 1.25G Assets:Tajer -1.25G 0 06-Mar-15 Auction House Expenses:Fees:Auction 23.75s 23.75s Expenses:Fees:Auction -23.75s 0 06-Mar-15 Auction House Assets:Danell 4c 4c Equity:Gold -4c 0 06-Mar-15 Transfer Assets:Gruulmorg 2c 2c Assets:Danell -2c 0 06-Mar-15 Transfer Assets:Danell 42.50s 42.50s Expenses:Fees:Auction 7.50s 50.00s Assets:Gruulmorg -50.00s 0 06-Mar-15 Post Expenses:Fees:Mail 60c 60c Assets:Danell -60c 0 06-Mar-15 Player Assets:Tajer 3.00G 3.00G Equity:Gold -3.00G 0 06-Mar-15 Post Assets:Wyshona 40.00s 40.00s Expenses:Fees:Mail 30c 40.30s Assets:Danell -40.30s 0 06-Mar-15 Auction House Expenses:Fees:Auction 8.00s 8.00s Expenses:Fees:Auction 11.00s 19.00s Assets:Wyshona -19.00s 0 06-Mar-15 Auction House Assets:Tajer:Items 65.00G 65.00G Assets:Tajer -65.00G 0 06-Mar-15 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-15 Auction House Expenses:Fees:Auction 82.68s 82.68s Assets:Tajer -82.68s 0 06-Mar-15 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-15 Vendor Assets:Tajer 1.67G 1.67G Assets:Tajer 1.66G 3.34G Equity:Gold -3.34G 0 06-Mar-15 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-15 Auction House Expenses:Fees:Auction 7.72s 7.72s Expenses:Fees:Auction 5.44s 13.16s Expenses:Fees:Auction 4.44s 17.60s Expenses:Fees:Auction 4.32s 21.92s Expenses:Fees:Auction 2.04s 23.96s Assets:Tajer -23.96s 0 06-Mar-15 Player Assets:Tajer 12.00s 12.00s Equity:Gold -12.00s 0 06-Mar-15 Vendor Assets:Tajer 22.00s 22.00s Equity:Gold -22.00s 0 06-Mar-15 Auction House Assets:Tajer:Items 1.00G 1.00G Assets:Tajer -1.00G 0 06-Mar-15 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-15 Auction House Expenses:Fees:Auction 82.68s 82.68s Assets:Tajer -82.68s 0 06-Mar-15 Auction House Assets:Tajer:Items 2.11G 2.11G Assets:Tajer -2.11G 0 06-Mar-15 Auction House Assets:Tajer:Items 2.30G 2.30G Assets:Tajer -2.30G 0 06-Mar-15 Auction House Assets:Tajer:Items 1.50G 1.50G Assets:Tajer -1.50G 0 06-Mar-16 Player Assets:Tajer 3.00G 3.00G Equity:Gold -3.00G 0 06-Mar-16 Post Expenses:Fees:Mail 90c 90c Assets:Tajer -90c 0 06-Mar-16 Auction House Assets:Tajer 119.58G 119.58G Assets:Tajer:Items -65.00G 54.58G Income:Brokering -54.58G 0 06-Mar-16 Auction House Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 "Plans: Mithril Shield Spike" 1 Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 "Plans: Mithril Shield Spike" 2 Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 "Plans: Mithril Shield Spike" 2 "Recipe: Elixir of Giant Growth" 1 Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 "Plans: Mithril Shield Spike" 2 "Recipe: Elixir of Giant Growth" 2 Assets:Tajer:Items "Plans: Mithril Shield Spike" -1 "Plans: Mithril Shield Spike" 1 "Recipe: Elixir of Giant Growth" 2 Assets:Tajer:Items "Plans: Mithril Shield Spike" -1 "Recipe: Elixir of Giant Growth" 2 Assets:Tajer:Items "Recipe: Elixir of Giant Growth" -1 "Recipe: Elixir of Giant Growth" 1 Assets:Tajer:Items "Recipe: Elixir of Giant Growth" -1 0 06-Mar-16 Player Assets:Tajer 4.00G 4.00G Equity:Gold -4.00G 0 06-Mar-16 Auction House Assets:Wyshona 13.41G 13.41G Equity:Gold -13.41G 0 06-Mar-16 Auction House Assets:Gruulmorg 4c 4c Assets:Danell -4c 0 06-Mar-16 Auction House Expenses:Fees:Auction 4c 4c Expenses:Fees:Mail 1.20s 1.24s Assets:Danell -1.24s 0 06-Mar-16 Auction House Expenses:Fees:Auction 24.00s 24.00s Expenses:Fees:Auction 12.00s 36.00s Expenses:Fees:Auction 12.00s 48.00s Expenses:Fees:Auction 84c 48.84s Expenses:Fees:Auction 84c 49.68s Assets:Wyshona -49.68s 0 06-Mar-16 Crazy Cat Lady Expenses:Items 40.00s 40.00s Expenses:Items 40.00s 80.00s Assets:Wyshona -80.00s 0 06-Mar-16 Transfer Expenses:Fees:Mail 60c 60c Assets:Danell 5.00G 5.01G Assets:Wyshona -5.01G 0 06-Mar-16 Transfer Expenses:Fees:Mail 30c 30c Assets:Tajer 20.00G 20.00G Assets:Gruulmorg -20.00G 0 06-Mar-16 Auction House Assets:Tajer:Items 1.00G 1.00G Assets:Tajer -1.00G 0 06-Mar-16 Auction House Expenses:Fees:Auction 9.36s 9.36s Assets:Tajer -9.36s 0 06-Mar-16 Transfer Expenses:Fees:Mail 30c 30c Assets:Gruulmorg 30.00G 30.00G Assets:Tajer -30.00G 0 06-Mar-16 Auction House Assets:Gruulmorg:Items 30.00G 30.00G Assets:Gruulmorg -30.00G 0 06-Mar-16 Transfer Assets:Tajer:Items "Ace of Warlords" 2 "Ace of Warlords" 2 Assets:Gruulmorg:Items "Ace of Warlords" -2 0 06-Mar-16 Post Expenses:Fees:Mail 60c 60c Assets:Gruulmorg -60c 0 06-Mar-16 Post Expenses:Fees:Mail 1.20s 1.20s Assets:Tajer -1.20s 0 06-Mar-16 Auction House Assets:Tajer 21.04s 21.04s Equity:Gold -21.04s 0 06-Mar-16 Auction House Expenses:Fees:Auction 75.00s 75.00s Expenses:Fees:Auction 75.00s 1.50G Assets:Tajer -1.50G 0 06-Mar-16 Transfer Assets:Danell 6c 6c Assets:Gruulmorg -6c 0 06-Mar-16 Post Expenses:Fees:Mail 60c 60c Assets:Gruulmorg -60c 0 06-Mar-16 Post Expenses:Fees:Mail 60c 60c Assets:Tajer -60c 0 06-Mar-16 General Goods Vendor Expenses:Items 50c 50c Assets:Tajer -50c 0 06-Mar-16 Player Assets:Tajer 1.00G 1.00G Equity:Gold -1.00G 0 06-Mar-17 Auction House Assets:Wyshona 18.06G 18.06G Assets:Wyshona:Items -1.50G 16.56G Income:Brokering -16.56G 0 06-Mar-17 Auction House Assets:Wyshona 18.06G 18.06G Assets:Wyshona:Items -1.00G 17.06G Income:Brokering -17.06G 0 06-Mar-17 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-17 Player: raev Assets:Tajer:Items 30.00G 30.00G Assets:Tajer:Items -30.00G 0 06-Mar-17 Auction House Expenses:Fees:Auction 74.82s 74.82s Assets:Tajer -74.82s 0 06-Mar-17 Post Expenses:Fees:Mail 3.00s 3.00s Assets:Wyshona -3.00s 0 06-Mar-17 Player Assets:Wyshona 1.00G 1.00G Assets:Wyshona:Items -1.25G -25.00s Expenses:Capital Loss 25.00s 0 06-Mar-17 Auction House (impl.. Expenses:Items 2.79G 2.79G Assets:Wyshona -2.79G 0 06-Mar-17 Auction House (impl.. Assets:Danell:Items 3.00G 3.00G Assets:Tajer:Items 3.90G 6.90G Assets:Tajer:Items 2.00G 8.90G Assets:Tajer:Items 4.00G 12.90G Assets:Tajer:Items 4.00G 16.90G Assets:Tajer:Items 5.00G 21.90G Assets:Tajer:Items 4.00G 25.90G Assets:Tajer:Items 1.00G 26.90G Assets:Wyshona -26.90G 0 06-Mar-17 Auction House Assets:Tajer 4.00G 4.00G Assets:Tajer:Items -1.00G 3.00G Income:Brokering -3.00G 0 06-Mar-17 Auction House Assets:Danell 31.71s 31.71s Assets:Danell:Items -3.00G -2.68G Expenses:Capital Loss 2.68G 0 06-Mar-17 Auction House Expenses:Fees:Auction 1.25G 1.25G Assets:Danell -1.25G 0 06-Mar-17 Transfer Assets:Gruulmorg 15.00G 15.00G Expenses:Fees:Mail 30c 15.00G Assets:Tajer -15.00G 0 06-Mar-17 Auction House Assets:Wyshona 36.25G 36.25G Assets:Wyshona:Items -2.11G 34.14G Assets:Wyshona:Items -2.30G 31.84G Income:Brokering -31.84G 0 06-Mar-17 Transfer Assets:Danell 49.96G 49.96G Expenses:Gifts 1.00G 50.96G Expenses:Fees:Mail 30c 50.96G Assets:Wyshona -50.96G 0 06-Mar-17 Post Expenses:Fees:Mail 90c 90c Expenses:Fees:Auction 1.66s 2.56s Assets:Gruulmorg -2.56s 0 06-Mar-17 Transfer Assets:Gruulmorg 45.92G 45.92G Expenses:Fees:Auction 8.10G 54.02G Assets:Danell -54.02G 0 06-Mar-17 Transfer Assets:Tajer 66.25G 66.25G Expenses:Fees:Mail 30c 66.25G Assets:Gruulmorg -66.25G 0 06-Mar-17 Auction House Expenses:Fees:Auction 75.00s 75.00s Expenses:Fees:Mail 30c 75.30s Assets:Tajer -75.30s 0 06-Mar-18 Auction House Assets:Tajer 15.00G 15.00G Assets:Tajer:Items -3.90G 11.10G Income:Brokering -11.10G 0 06-Mar-18 Auction House Assets:Tajer 43.45G 43.45G Assets:Tajer:Items -30.00G 13.45G Income:Brokering -13.45G 0 06-Mar-18 Auction House Assets:Tajer 19.95G 19.95G Assets:Tajer:Items -5.00G 14.95G Income:Brokering -14.95G 0 06-Mar-19 Auction House Assets:Tajer 28.50G 28.50G Assets:Tajer:Items -4.00G 24.50G Income:Brokering -24.50G 0 06-Mar-19 Auction House Assets:Tajer 17.10G 17.10G Assets:Tajer:Items -4.00G 13.10G Income:Brokering -13.10G 0 06-Mar-19 Auction House Assets:Tajer 4.65G 4.65G Assets:Tajer:Items -1.00G 3.65G Income:Brokering -3.65G 0 06-Mar-19 Auction House Assets:Tajer:Items 3.00G 3.00G Assets:Tajer:Items 2.50G 5.50G Assets:Tajer -5.50G 0 06-Mar-20 Auction House Assets:Tajer 16.34G 16.34G Assets:Tajer:Items -2.00G 14.34G Income:Brokering -14.34G 0 06-Mar-20 Auction House Assets:Tajer 5.00G 5.00G Assets:Tajer:Items -3.00G 2.00G Income:Brokering -2.00G 0 06-Mar-20 Auction House Assets:Tajer 15.00G 15.00G Assets:Tajer:Items -4.00G 11.00G Income:Brokering -11.00G 0 06-Mar-20 Auction House Expenses:Fees:Mail 60c 60c Assets:Tajer -60c 0 06-Mar-21 Auction House Assets:Tajer:Items 170.00G 170.00G Assets:Tajer -170.00G 0 06-Mar-21 Auction House Expenses:Fees:Auction 27.60s 27.60s Expenses:Fees:Auction 27.60s 55.20s Assets:Tajer -55.20s 0 06-Mar-22 Auction House Assets:Tajer:Items 200.00G 200.00G Assets:Tajer -200.00G 0 06-Mar-22 Auction House Expenses:Fees:Auction 1.77G 1.77G Expenses:Fees:Auction 1.77G 3.54G Expenses:Fees:Auction 1.77G 5.31G Expenses:Fees:Auction 1.77G 7.08G Expenses:Fees:Auction 75.00s 7.83G Assets:Tajer -7.83G 0 06-Mar-23 Auction House Assets:Tajer 166.53G 166.53G Assets:Tajer:Items -170.00G -3.47G Expenses:Capital Loss 3.47G 0 06-Mar-26 Auction House Assets:Tajer 8.20G 8.20G Assets:Tajer:Items -2.50G 5.70G Income:Brokering -5.70G 0 06-Mar-26 Player Expenses:Items 1.50G 1.50G Expenses:Fees:Mail 30c 1.50G Expenses:Fees:Mail 30c 1.51G Assets:Tajer -1.51G 0 06-Mar-27 Player Assets:Tajer 160.00G 160.00G Assets:Tajer:Items -200.00G -40.00G Expenses:Capital Loss 40.00G 0 06-Mar-27 Player Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-26 Player Expenses:Items 54.00G 54.00G Expenses:Items 10.00G 64.00G Expenses:Fees:Bank 10.00G 74.00G Expenses:Fees:Mail 6.30s 74.06G Expenses:Fees:Mail 3.30s 74.10G Expenses:Fees:Mail 30c 74.10G Assets:Tajer -74.10G 0 06-Apr-01 Auction House Assets:Tajer:Items 155.00G 155.00G Assets:Tajer -155.00G 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-lots_basis_base.test�������������������������������������������������0000664�0000000�0000000�00000121413�14411236400�0022354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������C 1.00s = 100c C 1.00G = 100s D 1.00G 2006/03/14 Opening Balances Assets:Tajer 1339829c Assets:Gruulmorg 248720c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1428c Expenses:Fees:Auction 768c Expenses:Fees:Auction 612c Expenses:Fees:Auction 4764c Expenses:Fees:Auction 3372c Expenses:Fees:Auction 1296c Expenses:Fees:Auction 1332c Expenses:Fees:Auction 660c Expenses:Fees:Auction 10044c Expenses:Fees:Auction 3588c Expenses:Fees:Auction 1632c Expenses:Fees:Auction 8388c Expenses:Fees:Auction 9984c Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 158860c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1320c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 11496c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 3216c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/14 Auction House Assets:Tajer 34678c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Gruulmorg 1G Equity:Gold 2006/03/14 Auction House Assets:Tajer 59389c Equity:Gold 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Player Assets:Tajer 3G Equity:Gold 2006/03/14 Player Assets:Tajer 6G Equity:Gold 2006/03/14 Auction House Expenses:Items 35s Assets:Tajer 2006/03/14 Auction House Assets:Tajer:Items "Plans: Wildthorn Mail" 1 @ 125s Assets:Tajer 2006/03/14 Auction House Assets:Bids 259c Assets:Bids 45s Assets:Bids 4720c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Auction House Expenses:Items 8G Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Puldoost Assets:Tajer 8G Expenses:Items 2006/03/14 Auction House Assets:Wyshona:Items "Plans: Wildthorn Mail" 1 {1.25G} Assets:Tajer:Items 2006/03/15 Auction House Assets:Tajer 45s Assets:Tajer 259c Assets:Bids 2006/03/15 Auction House Assets:Tajer 4720c Assets:Bids 2006/03/15 Auction House Expenses:Fees:Auction 12542c ; something got lost here Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 2375c Expenses:Fees:Auction -2375c Assets:Gruulmorg 2006/03/15 Auction House Assets:Danell 4c Equity:Gold 2006/03/15 Transfer Assets:Gruulmorg 2c Assets:Danell 2006/03/15 Transfer Assets:Danell 4250c Expenses:Fees:Auction 750c Assets:Gruulmorg -50s 2006/03/15 Post Expenses:Fees:Mail 60c Assets:Danell 2006/03/15 Player Assets:Tajer 3G Equity:Gold 2006/03/15 Post Assets:Wyshona 40s Expenses:Fees:Mail 30c Assets:Danell 2006/03/15 Auction House Expenses:Fees:Auction 8s Expenses:Fees:Auction 11s Assets:Wyshona 2006/03/15 Auction House Assets:Tajer:Items "Beaststalker's Belt" 1 @ 65G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Vendor Assets:Tajer 16744c Assets:Tajer 16640c Equity:Gold 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 772c Expenses:Fees:Auction 544c Expenses:Fees:Auction 444c Expenses:Fees:Auction 432c Expenses:Fees:Auction 204c Assets:Tajer 2006/03/15 Player Assets:Tajer 12s Equity:Gold 2006/03/15 Vendor Assets:Tajer 22s Equity:Gold 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 1G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 21050c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 23000c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 150s Assets:Tajer 2006/03/16 Player Assets:Tajer 3G Equity:Gold 2006/03/16 Post Expenses:Fees:Mail 90c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 1195768c Assets:Tajer:Items "Beaststalker's Belt" -1 {65G} @ 1195768c Income:Brokering -545768c 2006/03/16 Auction House Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {21050c} Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {2.3G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1.5G} Assets:Tajer:Items 2006/03/16 Player Assets:Tajer 4G Equity:Gold 2006/03/16 Auction House Assets:Wyshona 1341s Equity:Gold 2006/03/16 Auction House Assets:Gruulmorg 4c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 4c Expenses:Fees:Mail 120c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 24s Expenses:Fees:Auction 12s Expenses:Fees:Auction 12s Expenses:Fees:Auction 84c Expenses:Fees:Auction 84c Assets:Wyshona 2006/03/16 Crazy Cat Lady Expenses:Items 40s Expenses:Items 40s Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 60c Assets:Danell 5G Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Tajer 20G Assets:Gruulmorg 2006/03/16 Auction House Assets:Tajer:Items "Pulsating Hydra Heart" 1 @ 1G Assets:Tajer 2006/03/16 Auction House Expenses:Fees:Auction 936c Assets:Tajer 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Gruulmorg 30G Assets:Tajer 2006/03/16 Auction House Assets:Gruulmorg:Items "Ace of Warlords" 2 @ 15G Assets:Gruulmorg 2006/03/16 Transfer Assets:Tajer:Items "Ace of Warlords" 2 {15G} Assets:Gruulmorg:Items 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 2104c Equity:Gold 2006/03/16 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/16 Transfer Assets:Danell 6c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Tajer 2006/03/16 General Goods Vendor Expenses:Items 50c ; wrapping paper Assets:Tajer 2006/03/16 Player Assets:Tajer 1G Equity:Gold 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1.5G} @ 180584c Income:Brokering -165584c 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1G} @ 180584c Income:Brokering -170584c 2006/03/17 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Player: raev Assets:Tajer:Items "Wildheart Belt" 1 @ 30G Assets:Tajer:Items "Ace of Warlords" -2 @ 15G 2006/03/17 Auction House Expenses:Fees:Auction 7482c Assets:Tajer 2006/03/17 Post Expenses:Fees:Mail 300c Assets:Wyshona 2006/03/17 Player Assets:Wyshona 1G Assets:Wyshona:Items "Plans: Wildthorn Mail" -1 {1.25G} @ 1G Expenses:Capital Loss 25s 2006/03/17 Auction House (implicit transfer) Expenses:Items 279s ; Recipe: Swiftness Potion Assets:Wyshona 2006/03/17 Auction House (implicit transfer) Assets:Danell:Items "Ace of Warlords" 1 @ 3G Assets:Tajer:Items "Ace of Warlords" 1 @ 3.9G Assets:Tajer:Items "Holy Bologna" 1 @ 2G Assets:Tajer:Items "The Emerald Dream" 1 @ 4G Assets:Tajer:Items "The Arcanist's Cookbook" 1 @ 4G Assets:Tajer:Items "Harnessing Shadows" 1 @ 5G Assets:Tajer:Items "Garona: Book on Treachery" 1 @ 4G Assets:Tajer:Items "Preserved Holly" 5 @ 20s Assets:Wyshona 2006/03/17 Auction House Assets:Tajer 4G Assets:Tajer:Items "Pulsating Hydra Heart" -1 {1G} @ 4G Income:Brokering -3G 2006/03/17 Auction House Assets:Danell 3171c Assets:Danell:Items "Ace of Warlords" -1 {3G} @ 3171c Expenses:Capital Loss 26829c 2006/03/17 Auction House Expenses:Fees:Auction 12537c Assets:Danell 2006/03/17 Transfer Assets:Gruulmorg 15G Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Auction House Assets:Wyshona 362450c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {21050c} @ 181225c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {2.3G} @ 181225c Income:Brokering -318400c 2006/03/17 Transfer Assets:Danell 499560c Expenses:Gifts 1G Expenses:Fees:Mail 30c Assets:Wyshona 2006/03/17 Post Expenses:Fees:Mail 90c Expenses:Fees:Auction 166c Assets:Gruulmorg 2006/03/17 Transfer Assets:Gruulmorg 459211c Expenses:Fees:Auction 81023c Assets:Danell -540234c 2006/03/17 Transfer Assets:Tajer 662465c Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/17 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Mail 30c Assets:Tajer 2006/03/18 Auction House Assets:Tajer 15G Assets:Tajer:Items "Ace of Warlords" -1 {3.9G} @ 15G Income:Brokering -111000c 2006/03/18 Auction House Assets:Tajer 434472c Assets:Tajer:Items "Wildheart Belt" -1 {30G} @ 434472c Income:Brokering -134472c 2006/03/18 Auction House Assets:Tajer 1995s Assets:Tajer:Items "Harnessing Shadows" -1 {5G} @ 1995s Income:Brokering -1495s 2006/03/19 Auction House Assets:Tajer 2850s Assets:Tajer:Items "Garona: Book on Treachery" -1 {4G} @ 2850s Income:Brokering -2450s 2006/03/19 Auction House Assets:Tajer 1710s Assets:Tajer:Items "The Arcanist's Cookbook" -1 {4G} @ 1710s Income:Brokering -1310s 2006/03/19 Auction House Assets:Tajer 46550c Assets:Tajer:Items "Preserved Holly" -5 {20s} @ 9310c Income:Brokering -36550c 2006/03/19 Auction House Assets:Tajer:Items "Two of Portals" 1 @ 3G Assets:Tajer:Items "Two of Portals" 1 @ 2.5G Assets:Tajer 2006/03/20 Auction House Assets:Tajer 163443c Assets:Tajer:Items "Holy Bologna" -1 {2G} @ 163443c Income:Brokering -143443c 2006/03/20 Auction House Assets:Tajer 5G Assets:Tajer:Items "Two of Portals" -1 {3G} @ 5G Income:Brokering -2G 2006/03/20 Auction House Assets:Tajer 15G Assets:Tajer:Items "The Emerald Dream" -1 {4G} @ 15G Income:Brokering -11G 2006/03/20 Auction House Expenses:Fees:Mail 60c Assets:Tajer 2006/03/21 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 170G Assets:Tajer 2006/03/21 Auction House Expenses:Fees:Auction 2760c Expenses:Fees:Auction 2760c Assets:Tajer 2006/03/22 Auction House Assets:Tajer:Items "Nightblade" 1 @ 200G Assets:Tajer 2006/03/22 Auction House Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/23 Auction House Assets:Tajer 1665260c Assets:Tajer:Items "Orb of Deception" -1 {170G} @ 1665260c Expenses:Capital Loss 34740c 2006/03/26 Auction House Assets:Tajer 81980c Assets:Tajer:Items "Two of Portals" -1 {2.5G} @ 81980c Income:Brokering -56980c 2006/03/26 Player Expenses:Items 150s ; Recipe: Elixir of Minor Agility Expenses:Fees:Mail 30c Expenses:Fees:Mail 30c Assets:Tajer 2006/03/27 Player Assets:Tajer 160G Assets:Tajer:Items "Nightblade" -1 {200G} @ 160G Expenses:Capital Loss 40G 2006/03/27 Player Expenses:Fees:Mail 30c Assets:Tajer 2006/03/26 Player Expenses:Items (9G * 6) ; Traveler's backpacks Expenses:Items 10G Expenses:Fees:Bank 10G Expenses:Fees:Mail 630c Expenses:Fees:Mail 330c Expenses:Fees:Mail 30c Assets:Tajer 2006/04/01 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 155G Assets:Tajer test reg --basis --base 06-Mar-14 Opening Balances Assets:Tajer 1339829c 1339829c Assets:Gruulmorg 248720c 1588549c Equity:Gold -1588549c 0 06-Mar-14 Auction House Expenses:Fees:Auction 1428c 1428c Expenses:Fees:Auction 768c 2196c Expenses:Fees:Auction 612c 2808c Expenses:Fees:Auction 4764c 7572c Expenses:Fees:Auction 3372c 10944c Expenses:Fees:Auction 1296c 12240c Expenses:Fees:Auction 1332c 13572c Expenses:Fees:Auction 660c 14232c Expenses:Fees:Auction 10044c 24276c Expenses:Fees:Auction 3588c 27864c Expenses:Fees:Auction 1632c 29496c Expenses:Fees:Auction 8388c 37884c Expenses:Fees:Auction 9984c 47868c Expenses:Fees:Auction 2316c 50184c Assets:Tajer -50184c 0 06-Mar-14 Auction House Assets:Tajer 158860c 158860c Equity:Gold -158860c 0 06-Mar-14 Auction House Expenses:Fees:Auction 1320c 1320c Assets:Tajer -1320c 0 06-Mar-14 Auction House Assets:Tajer 11496c 11496c Equity:Gold -11496c 0 06-Mar-14 Auction House Expenses:Fees:Auction 3216c 3216c Assets:Tajer -3216c 0 06-Mar-14 Post Expenses:Fees:Mail 30c 30c Assets:Gruulmorg -30c 0 06-Mar-14 Auction House Assets:Tajer 34678c 34678c Equity:Gold -34678c 0 06-Mar-14 Auction House Expenses:Fees:Auction 2316c 2316c Assets:Tajer -2316c 0 06-Mar-14 Auction House Assets:Gruulmorg 10000c 10000c Equity:Gold -10000c 0 06-Mar-14 Auction House Assets:Tajer 59389c 59389c Equity:Gold -59389c 0 06-Mar-14 Post Expenses:Fees:Mail 120c 120c Assets:Tajer -120c 0 06-Mar-14 Player Assets:Tajer 30000c 30000c Equity:Gold -30000c 0 06-Mar-14 Player Assets:Tajer 60000c 60000c Equity:Gold -60000c 0 06-Mar-14 Auction House Expenses:Items 3500c 3500c Assets:Tajer -3500c 0 06-Mar-14 Auction House Assets:Tajer:Items 12500c 12500c Assets:Tajer -12500c 0 06-Mar-14 Auction House Assets:Bids 259c 259c Assets:Bids 4500c 4759c Assets:Bids 4720c 9479c Assets:Tajer -9479c 0 06-Mar-14 Post Expenses:Fees:Mail 120c 120c Assets:Tajer -120c 0 06-Mar-14 Auction House Expenses:Items 80000c 80000c Assets:Tajer -80000c 0 06-Mar-14 Post Expenses:Fees:Mail 120c 120c Assets:Tajer -120c 0 06-Mar-14 Puldoost Assets:Tajer 80000c 80000c Expenses:Items -80000c 0 06-Mar-14 Auction House Assets:Wyshona:Items "Plans: Wildthorn Mail" 1 "Plans: Wildthorn Mail" 1 Assets:Tajer:Items "Plans: Wildthorn Mail" -1 0 06-Mar-15 Auction House Assets:Tajer 4500c 4500c Assets:Tajer 259c 4759c Assets:Bids -4759c 0 06-Mar-15 Auction House Assets:Tajer 4720c 4720c Assets:Bids -4720c 0 06-Mar-15 Auction House Expenses:Fees:Auction 12542c 12542c Assets:Tajer -12542c 0 06-Mar-15 Auction House Expenses:Fees:Auction 2375c 2375c Expenses:Fees:Auction -2375c 0 06-Mar-15 Auction House Assets:Danell 4c 4c Equity:Gold -4c 0 06-Mar-15 Transfer Assets:Gruulmorg 2c 2c Assets:Danell -2c 0 06-Mar-15 Transfer Assets:Danell 4250c 4250c Expenses:Fees:Auction 750c 5000c Assets:Gruulmorg -5000c 0 06-Mar-15 Post Expenses:Fees:Mail 60c 60c Assets:Danell -60c 0 06-Mar-15 Player Assets:Tajer 30000c 30000c Equity:Gold -30000c 0 06-Mar-15 Post Assets:Wyshona 4000c 4000c Expenses:Fees:Mail 30c 4030c Assets:Danell -4030c 0 06-Mar-15 Auction House Expenses:Fees:Auction 800c 800c Expenses:Fees:Auction 1100c 1900c Assets:Wyshona -1900c 0 06-Mar-15 Auction House Assets:Tajer:Items 650000c 650000c Assets:Tajer -650000c 0 06-Mar-15 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-15 Auction House Expenses:Fees:Auction 8268c 8268c Assets:Tajer -8268c 0 06-Mar-15 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-15 Vendor Assets:Tajer 16744c 16744c Assets:Tajer 16640c 33384c Equity:Gold -33384c 0 06-Mar-15 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-15 Auction House Expenses:Fees:Auction 772c 772c Expenses:Fees:Auction 544c 1316c Expenses:Fees:Auction 444c 1760c Expenses:Fees:Auction 432c 2192c Expenses:Fees:Auction 204c 2396c Assets:Tajer -2396c 0 06-Mar-15 Player Assets:Tajer 1200c 1200c Equity:Gold -1200c 0 06-Mar-15 Vendor Assets:Tajer 2200c 2200c Equity:Gold -2200c 0 06-Mar-15 Auction House Assets:Tajer:Items 10000c 10000c Assets:Tajer -10000c 0 06-Mar-15 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-15 Auction House Expenses:Fees:Auction 8268c 8268c Assets:Tajer -8268c 0 06-Mar-15 Auction House Assets:Tajer:Items 21050c 21050c Assets:Tajer -21050c 0 06-Mar-15 Auction House Assets:Tajer:Items 23000c 23000c Assets:Tajer -23000c 0 06-Mar-15 Auction House Assets:Tajer:Items 15000c 15000c Assets:Tajer -15000c 0 06-Mar-16 Player Assets:Tajer 30000c 30000c Equity:Gold -30000c 0 06-Mar-16 Post Expenses:Fees:Mail 90c 90c Assets:Tajer -90c 0 06-Mar-16 Auction House Assets:Tajer 1195768c 1195768c Assets:Tajer:Items -650000c 545768c Income:Brokering -545768c 0 06-Mar-16 Auction House Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 "Plans: Mithril Shield Spike" 1 Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 "Plans: Mithril Shield Spike" 2 Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 "Plans: Mithril Shield Spike" 2 "Recipe: Elixir of Giant Growth" 1 Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 "Plans: Mithril Shield Spike" 2 "Recipe: Elixir of Giant Growth" 2 Assets:Tajer:Items "Plans: Mithril Shield Spike" -1 "Plans: Mithril Shield Spike" 1 "Recipe: Elixir of Giant Growth" 2 Assets:Tajer:Items "Plans: Mithril Shield Spike" -1 "Recipe: Elixir of Giant Growth" 2 Assets:Tajer:Items "Recipe: Elixir of Giant Growth" -1 "Recipe: Elixir of Giant Growth" 1 Assets:Tajer:Items "Recipe: Elixir of Giant Growth" -1 0 06-Mar-16 Player Assets:Tajer 40000c 40000c Equity:Gold -40000c 0 06-Mar-16 Auction House Assets:Wyshona 134100c 134100c Equity:Gold -134100c 0 06-Mar-16 Auction House Assets:Gruulmorg 4c 4c Assets:Danell -4c 0 06-Mar-16 Auction House Expenses:Fees:Auction 4c 4c Expenses:Fees:Mail 120c 124c Assets:Danell -124c 0 06-Mar-16 Auction House Expenses:Fees:Auction 2400c 2400c Expenses:Fees:Auction 1200c 3600c Expenses:Fees:Auction 1200c 4800c Expenses:Fees:Auction 84c 4884c Expenses:Fees:Auction 84c 4968c Assets:Wyshona -4968c 0 06-Mar-16 Crazy Cat Lady Expenses:Items 4000c 4000c Expenses:Items 4000c 8000c Assets:Wyshona -8000c 0 06-Mar-16 Transfer Expenses:Fees:Mail 60c 60c Assets:Danell 50000c 50060c Assets:Wyshona -50060c 0 06-Mar-16 Transfer Expenses:Fees:Mail 30c 30c Assets:Tajer 200000c 200030c Assets:Gruulmorg -200030c 0 06-Mar-16 Auction House Assets:Tajer:Items 10000c 10000c Assets:Tajer -10000c 0 06-Mar-16 Auction House Expenses:Fees:Auction 936c 936c Assets:Tajer -936c 0 06-Mar-16 Transfer Expenses:Fees:Mail 30c 30c Assets:Gruulmorg 300000c 300030c Assets:Tajer -300030c 0 06-Mar-16 Auction House Assets:Gruulmorg:Items 300000c 300000c Assets:Gruulmorg -300000c 0 06-Mar-16 Transfer Assets:Tajer:Items "Ace of Warlords" 2 "Ace of Warlords" 2 Assets:Gruulmorg:Items "Ace of Warlords" -2 0 06-Mar-16 Post Expenses:Fees:Mail 60c 60c Assets:Gruulmorg -60c 0 06-Mar-16 Post Expenses:Fees:Mail 120c 120c Assets:Tajer -120c 0 06-Mar-16 Auction House Assets:Tajer 2104c 2104c Equity:Gold -2104c 0 06-Mar-16 Auction House Expenses:Fees:Auction 7500c 7500c Expenses:Fees:Auction 7500c 15000c Assets:Tajer -15000c 0 06-Mar-16 Transfer Assets:Danell 6c 6c Assets:Gruulmorg -6c 0 06-Mar-16 Post Expenses:Fees:Mail 60c 60c Assets:Gruulmorg -60c 0 06-Mar-16 Post Expenses:Fees:Mail 60c 60c Assets:Tajer -60c 0 06-Mar-16 General Goods Vendor Expenses:Items 50c 50c Assets:Tajer -50c 0 06-Mar-16 Player Assets:Tajer 10000c 10000c Equity:Gold -10000c 0 06-Mar-17 Auction House Assets:Wyshona 180584c 180584c Assets:Wyshona:Items -15000c 165584c Income:Brokering -165584c 0 06-Mar-17 Auction House Assets:Wyshona 180584c 180584c Assets:Wyshona:Items -10000c 170584c Income:Brokering -170584c 0 06-Mar-17 Post Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-17 Player: raev Assets:Tajer:Items 300000c 300000c Assets:Tajer:Items -300000c 0 06-Mar-17 Auction House Expenses:Fees:Auction 7482c 7482c Assets:Tajer -7482c 0 06-Mar-17 Post Expenses:Fees:Mail 300c 300c Assets:Wyshona -300c 0 06-Mar-17 Player Assets:Wyshona 10000c 10000c Assets:Wyshona:Items -12500c -2500c Expenses:Capital Loss 2500c 0 06-Mar-17 Auction House (impl.. Expenses:Items 27900c 27900c Assets:Wyshona -27900c 0 06-Mar-17 Auction House (impl.. Assets:Danell:Items 30000c 30000c Assets:Tajer:Items 39000c 69000c Assets:Tajer:Items 20000c 89000c Assets:Tajer:Items 40000c 129000c Assets:Tajer:Items 40000c 169000c Assets:Tajer:Items 50000c 219000c Assets:Tajer:Items 40000c 259000c Assets:Tajer:Items 10000c 269000c Assets:Wyshona -269000c 0 06-Mar-17 Auction House Assets:Tajer 40000c 40000c Assets:Tajer:Items -10000c 30000c Income:Brokering -30000c 0 06-Mar-17 Auction House Assets:Danell 3171c 3171c Assets:Danell:Items -30000c -26829c Expenses:Capital Loss 26829c 0 06-Mar-17 Auction House Expenses:Fees:Auction 12537c 12537c Assets:Danell -12537c 0 06-Mar-17 Transfer Assets:Gruulmorg 150000c 150000c Expenses:Fees:Mail 30c 150030c Assets:Tajer -150030c 0 06-Mar-17 Auction House Assets:Wyshona 362450c 362450c Assets:Wyshona:Items -21050c 341400c Assets:Wyshona:Items -23000c 318400c Income:Brokering -318400c 0 06-Mar-17 Transfer Assets:Danell 499560c 499560c Expenses:Gifts 10000c 509560c Expenses:Fees:Mail 30c 509590c Assets:Wyshona -509590c 0 06-Mar-17 Post Expenses:Fees:Mail 90c 90c Expenses:Fees:Auction 166c 256c Assets:Gruulmorg -256c 0 06-Mar-17 Transfer Assets:Gruulmorg 459211c 459211c Expenses:Fees:Auction 81023c 540234c Assets:Danell -540234c 0 06-Mar-17 Transfer Assets:Tajer 662465c 662465c Expenses:Fees:Mail 30c 662495c Assets:Gruulmorg -662495c 0 06-Mar-17 Auction House Expenses:Fees:Auction 7500c 7500c Expenses:Fees:Mail 30c 7530c Assets:Tajer -7530c 0 06-Mar-18 Auction House Assets:Tajer 150000c 150000c Assets:Tajer:Items -39000c 111000c Income:Brokering -111000c 0 06-Mar-18 Auction House Assets:Tajer 434472c 434472c Assets:Tajer:Items -300000c 134472c Income:Brokering -134472c 0 06-Mar-18 Auction House Assets:Tajer 199500c 199500c Assets:Tajer:Items -50000c 149500c Income:Brokering -149500c 0 06-Mar-19 Auction House Assets:Tajer 285000c 285000c Assets:Tajer:Items -40000c 245000c Income:Brokering -245000c 0 06-Mar-19 Auction House Assets:Tajer 171000c 171000c Assets:Tajer:Items -40000c 131000c Income:Brokering -131000c 0 06-Mar-19 Auction House Assets:Tajer 46550c 46550c Assets:Tajer:Items -10000c 36550c Income:Brokering -36550c 0 06-Mar-19 Auction House Assets:Tajer:Items 30000c 30000c Assets:Tajer:Items 25000c 55000c Assets:Tajer -55000c 0 06-Mar-20 Auction House Assets:Tajer 163443c 163443c Assets:Tajer:Items -20000c 143443c Income:Brokering -143443c 0 06-Mar-20 Auction House Assets:Tajer 50000c 50000c Assets:Tajer:Items -30000c 20000c Income:Brokering -20000c 0 06-Mar-20 Auction House Assets:Tajer 150000c 150000c Assets:Tajer:Items -40000c 110000c Income:Brokering -110000c 0 06-Mar-20 Auction House Expenses:Fees:Mail 60c 60c Assets:Tajer -60c 0 06-Mar-21 Auction House Assets:Tajer:Items 1700000c 1700000c Assets:Tajer -1700000c 0 06-Mar-21 Auction House Expenses:Fees:Auction 2760c 2760c Expenses:Fees:Auction 2760c 5520c Assets:Tajer -5520c 0 06-Mar-22 Auction House Assets:Tajer:Items 2000000c 2000000c Assets:Tajer -2000000c 0 06-Mar-22 Auction House Expenses:Fees:Auction 17700c 17700c Expenses:Fees:Auction 17700c 35400c Expenses:Fees:Auction 17700c 53100c Expenses:Fees:Auction 17700c 70800c Expenses:Fees:Auction 7500c 78300c Assets:Tajer -78300c 0 06-Mar-23 Auction House Assets:Tajer 1665260c 1665260c Assets:Tajer:Items -1700000c -34740c Expenses:Capital Loss 34740c 0 06-Mar-26 Auction House Assets:Tajer 81980c 81980c Assets:Tajer:Items -25000c 56980c Income:Brokering -56980c 0 06-Mar-26 Player Expenses:Items 15000c 15000c Expenses:Fees:Mail 30c 15030c Expenses:Fees:Mail 30c 15060c Assets:Tajer -15060c 0 06-Mar-27 Player Assets:Tajer 1600000c 1600000c Assets:Tajer:Items -2000000c -400000c Expenses:Capital Loss 400000c 0 06-Mar-27 Player Expenses:Fees:Mail 30c 30c Assets:Tajer -30c 0 06-Mar-26 Player Expenses:Items 540000c 540000c Expenses:Items 100000c 640000c Expenses:Fees:Bank 100000c 740000c Expenses:Fees:Mail 630c 740630c Expenses:Fees:Mail 330c 740960c Expenses:Fees:Mail 30c 740990c Assets:Tajer -740990c 0 06-Apr-01 Auction House Assets:Tajer:Items 1550000c 1550000c Assets:Tajer -1550000c 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-market.test����������������������������������������������������������0000664�0000000�0000000�00000004366�14411236400�0020512�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/01/01 Sample 1a Assets:Brokerage:Stocks 100 S Assets:Brokerage:Cash -100 P P 2009/01/01 00:00:00 S 2 P 2009/02/01 Sample 2a Assets:Brokerage:Stocks 100 S @ 1 P Assets:Brokerage:Cash P 2009/02/01 00:00:00 S 4 P 2009/03/01 Sample 3a Assets:Brokerage:Stocks 100 S @@ 100 P Assets:Brokerage:Cash P 2009/03/01 00:00:00 S 8 P 2009/04/01 Sample 4a Assets:Brokerage:Cash 100 P Assets:Brokerage:Stocks -100 S {1 P} P 2009/04/01 00:00:00 S 16 P ; In this usage case, the top amount is always secondary ; 2010/01/01 Sample 1b ; Assets:Brokerage:Cash -100 P ; Assets:Brokerage:Stocks 100 S ; ; P 2010/01/01 00:00:00 S 2 P 2010/02/01 Sample 2b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @ 1 P P 2010/02/01 00:00:00 S 4 P 2010/03/01 Sample 3b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @@ 100 P P 2010/03/01 00:00:00 S 8 P 2010/04/01 Sample 4b Assets:Brokerage:Stocks -100 S {1 P} Assets:Brokerage:Cash 100 P P 2010/04/01 00:00:00 S 16 P test reg --market stocks 09-Jan-01 Sample 1a Asset:Brokerage:Stocks 200 P 200 P 09-Feb-01 Commodities revalued <Revalued> 200 P 400 P 09-Feb-01 Sample 2a Asset:Brokerage:Stocks 400 P 800 P 09-Mar-01 Commodities revalued <Revalued> 800 P 1600 P 09-Mar-01 Sample 3a Asset:Brokerage:Stocks 800 P 2400 P 09-Apr-01 Commodities revalued <Revalued> 2400 P 4800 P 09-Apr-01 Sample 4a Asset:Brokerage:Stocks -1600 P 3200 P 10-Feb-01 Commodities revalued <Revalued> -2400 P 800 P 10-Feb-01 Sample 2b Asset:Brokerage:Stocks 400 P 1200 P 10-Mar-01 Commodities revalued <Revalued> 1200 P 2400 P 10-Mar-01 Sample 3b Asset:Brokerage:Stocks 800 P 3200 P 10-Apr-01 Commodities revalued <Revalued> 3200 P 6400 P 10-Apr-01 Sample 4b Asset:Brokerage:Stocks -1600 P 4800 P end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-master-account.test��������������������������������������������������0000664�0000000�0000000�00000000617�14411236400�0022147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --master-account=Master $-0.35 0.350 VMMXX Master 0.350 VMMXX Assets:Investments:Vanguard:VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- $-0.35 0.350 VMMXX end test �����������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-meta-width.test������������������������������������������������������0000664�0000000�0000000�00000000731�14411236400�0021262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2004/05/27 (100) Credit card company ; This is an xact note! ; Sample: Value Liabilities:MasterCard $20.00 ; This is a posting note! ; Sample: Another Value ; :MyTag: Assets:Bank:Checking ; :AnotherTag: test reg --meta Sample --meta-width=15 Another Value 04-May-27 Credit card co.. Liabi:MasterCard $20.00 $20.00 Value As:Bank:Checking $-20.00 0 end test ���������������������������������������ledger-3.3.2/test/baseline/opt-meta.test������������������������������������������������������������0000664�0000000�0000000�00000000733�14411236400�0020147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2004/05/27 (100) Credit card company ; This is an xact note! ; Sample: Value Liabilities:MasterCard $20.00 ; This is a posting note! ; Sample: Another Value ; :MyTag: Assets:Bank:Checking ; :AnotherTag: test reg --meta Sample Another Value04-May-27 Credit card company Liabilities:MasterCard $20.00 $20.00 Value Assets:Bank:Checking $-20.00 0 end test �������������������������������������ledger-3.3.2/test/baseline/opt-monthly.test���������������������������������������������������������0000664�0000000�0000000�00000012612�14411236400�0020712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --monthly books 08-Jan-01 - 08-Jan-31 Expenses:Books $20.00 $20.00 08-Feb-01 - 08-Feb-29 Expenses:Books $40.00 $60.00 08-Mar-01 - 08-Mar-31 Expenses:Books $60.00 $120.00 08-Apr-01 - 08-Apr-30 Expenses:Books $80.00 $200.00 08-May-01 - 08-May-31 Expenses:Books $100.00 $300.00 08-Jun-01 - 08-Jun-30 Expenses:Books $120.00 $420.00 08-Jul-01 - 08-Jul-31 Expenses:Books $140.00 $560.00 08-Aug-01 - 08-Aug-31 Expenses:Books $160.00 $720.00 08-Sep-01 - 08-Sep-30 Expenses:Books $180.00 $900.00 08-Oct-01 - 08-Oct-31 Expenses:Books $200.00 $1100.00 08-Nov-01 - 08-Nov-30 Expenses:Books $220.00 $1320.00 08-Dec-01 - 08-Dec-31 Expenses:Books $240.00 $1560.00 09-Jan-01 - 09-Jan-31 Expenses:Books $20.00 $1580.00 09-Feb-01 - 09-Feb-28 Expenses:Books $40.00 $1620.00 09-Mar-01 - 09-Mar-31 Expenses:Books $60.00 $1680.00 09-Apr-01 - 09-Apr-30 Expenses:Books $80.00 $1760.00 09-May-01 - 09-May-31 Expenses:Books $100.00 $1860.00 09-Jun-01 - 09-Jun-30 Expenses:Books $120.00 $1980.00 09-Jul-01 - 09-Jul-31 Expenses:Books $140.00 $2120.00 09-Aug-01 - 09-Aug-31 Expenses:Books $160.00 $2280.00 09-Sep-01 - 09-Sep-30 Expenses:Books $180.00 $2460.00 09-Oct-01 - 09-Oct-31 Expenses:Books $200.00 $2660.00 09-Nov-01 - 09-Nov-30 Expenses:Books $220.00 $2880.00 09-Dec-01 - 09-Dec-31 Expenses:Books $240.00 $3120.00 end test ����������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-no-aliases.test������������������������������������������������������0000664�0000000�0000000�00000000702�14411236400�0021250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ alias A=Foo account Bar alias B 2001-01-01 * Test A 10 EUR B test reg 01-Jan-01 Test Foo 10 EUR 10 EUR Bar -10 EUR 0 end test test reg --no-aliases 01-Jan-01 Test A 10 EUR 10 EUR B -10 EUR 0 end test ��������������������������������������������������������������ledger-3.3.2/test/baseline/opt-no-revalued.test�����������������������������������������������������0000664�0000000�0000000�00000003435�14411236400�0021444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/01/01 Sample 1a Assets:Brokerage:Stocks 100 S Assets:Brokerage:Cash -100 P P 2009/01/01 00:00:00 S 2 P 2009/02/01 Sample 2a Assets:Brokerage:Stocks 100 S @ 1 P Assets:Brokerage:Cash P 2009/02/01 00:00:00 S 4 P 2009/03/01 Sample 3a Assets:Brokerage:Stocks 100 S @@ 100 P Assets:Brokerage:Cash P 2009/03/01 00:00:00 S 8 P 2009/04/01 Sample 4a Assets:Brokerage:Cash 100 P Assets:Brokerage:Stocks -100 S {1 P} P 2009/04/01 00:00:00 S 16 P ; In this usage case, the top amount is always secondary ; 2010/01/01 Sample 1b ; Assets:Brokerage:Cash -100 P ; Assets:Brokerage:Stocks 100 S ; ; P 2010/01/01 00:00:00 S 2 P 2010/02/01 Sample 2b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @ 1 P P 2010/02/01 00:00:00 S 4 P 2010/03/01 Sample 3b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @@ 100 P P 2010/03/01 00:00:00 S 8 P 2010/04/01 Sample 4b Assets:Brokerage:Stocks -100 S {1 P} Assets:Brokerage:Cash 100 P P 2010/04/01 00:00:00 S 16 P test reg --market --no-revalued stocks 09-Jan-01 Sample 1a Asset:Brokerage:Stocks 200 P 200 P 09-Feb-01 Sample 2a Asset:Brokerage:Stocks 400 P 800 P 09-Mar-01 Sample 3a Asset:Brokerage:Stocks 800 P 2400 P 09-Apr-01 Sample 4a Asset:Brokerage:Stocks -1600 P 3200 P 10-Feb-01 Sample 2b Asset:Brokerage:Stocks 400 P 1200 P 10-Mar-01 Sample 3b Asset:Brokerage:Stocks 800 P 3200 P 10-Apr-01 Sample 4b Asset:Brokerage:Stocks -1600 P 4800 P end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-no-rounding.test�����������������������������������������������������0000664�0000000�0000000�00000011474�14411236400�0021464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Opening balance Assets:Current 17.43 EUR Assets:Investments 200 "LU02" @ 24.77 EUR Assets:Investments 58 "LU02" @ 24.79900855 EUR Equity:Opening balance 2012-01-01 * Opening balance Assets:Pension 785.44 GBP Assets:Pension 97.0017 "H2" @ 5.342999720204 GBP Assets:Pension 4.3441 "H1" @ 5.289999915108 GBP Equity:Opening balance 2012-01-01 * Opening balance: misc Assets:Piggy bank 3.51 GBP Equity:Opening balance 2012-01-01 * Opening balance Assets:Rewards 9836 AAdvantage Equity:Opening balance 2012-01-03 * Receivable Assets:Current Assets:Receivable -161.06 EUR Assets:Receivable -9.99 GBP @@ 11.65 EUR 2012-01-27 * Test Income:Test -2759.50 GBP Income:Test -110.76 GBP Assets:Foo 345.57 GBP Expenses:Test 16.47 GBP Expenses:Test 6.33 GBP Expenses:Test 261.39 GBP Assets:Current test reg -X EUR -H --no-rounding 12-Jan-01 Opening balance Assets:Current 17.43 EUR 17.43 EUR Assets:Investments 4959.80 EUR 4977.23 EUR Assets:Investments 1438.34 EUR 6415.57 EUR Equity:Opening balance -6409.77 EUR 5.80 EUR 12-Jan-01 Opening balance Assets:Pension 785.44 GBP 5.80 EUR 785.44 GBP Assets:Pension 97.0017 H2 5.80 EUR 785.44 GBP 97.0017 H2 Assets:Pension 4.3441 H1 5.80 EUR 785.44 GBP 4.3441 H1 97.0017 H2 Equity:Opening balance -1326.70 GBP 5.80 EUR -541.26 GBP 4.3441 H1 97.0017 H2 12-Jan-01 Opening balance: misc Assets:Piggy bank 3.51 GBP 5.80 EUR -537.75 GBP 4.3441 H1 97.0017 H2 Equity:Opening balance -3.51 GBP 5.80 EUR -541.26 GBP 4.3441 H1 97.0017 H2 12-Jan-01 Opening balance Assets:Rewards 9836 AAdvantage 9836 AAdvantage 5.80 EUR -541.26 GBP 4.3441 H1 97.0017 H2 Equity:Opening balance -9836 AAdvantage 5.80 EUR -541.26 GBP 4.3441 H1 97.0017 H2 12-Jan-03 Commodities revalued <Revalued> 0 5.80 EUR 12-Jan-03 Receivable Assets:Current 172.71 EUR 178.51 EUR Assets:Receivable -161.06 EUR 17.45 EUR Assets:Receivable -11.65 EUR 5.80 EUR 12-Jan-27 Test Income:Test -3218.04 EUR -3212.23 EUR Income:Test -129.16 EUR -3341.40 EUR Assets:Foo 402.99 EUR -2938.41 EUR Expenses:Test 19.21 EUR -2919.20 EUR Expenses:Test 7.38 EUR -2911.82 EUR Expenses:Test 304.82 EUR -2606.99 EUR Assets:Current 2612.80 EUR 5.80 EUR end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-no-titles.test�������������������������������������������������������0000664�0000000�0000000�00000001331�14411236400�0021132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test reg -f test/input/drewr3.dat --no-titles --group-by payee reg food 11-Jan-02 Grocery Store Expense:Food:Groceries $ 65.00 $ 65.00 11-Jan-19 Grocery Store Expense:Food:Groceries $ 44.00 $ 109.00 10-Dec-20 Organic Co-op Expense:Food:Groceries $ 37.50 $ 37.50 Expense:Food:Groceries $ 37.50 $ 75.00 Expense:Food:Groceries $ 37.50 $ 112.50 Expense:Food:Groceries $ 37.50 $ 150.00 Expense:Food:Groceries $ 37.50 $ 187.50 Expense:Food:Groceries $ 37.50 $ 225.00 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-no-total.test��������������������������������������������������������0000664�0000000�0000000�00000000415�14411236400�0020753�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --no-total 0.350 VMMXX Assets:Investments:Vanguard:VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-now.test�������������������������������������������������������������0000664�0000000�0000000�00000000066�14411236400�0020023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ test eval today --now=2009/01/01 2009/01/01 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-only.test������������������������������������������������������������0000664�0000000�0000000�00000014241�14411236400�0020201�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg books --monthly --limit='amount > $100' 08-Nov-01 - 08-Nov-30 Expenses:Books $220.00 $220.00 08-Dec-01 - 08-Dec-31 Expenses:Books $240.00 $460.00 09-Nov-01 - 09-Nov-30 Expenses:Books $220.00 $680.00 09-Dec-01 - 09-Dec-31 Expenses:Books $240.00 $920.00 end test test reg books --monthly --only='amount > $100' 08-Jun-01 - 08-Jun-30 Expenses:Books $120.00 $120.00 08-Jul-01 - 08-Jul-31 Expenses:Books $140.00 $260.00 08-Aug-01 - 08-Aug-31 Expenses:Books $160.00 $420.00 08-Sep-01 - 08-Sep-30 Expenses:Books $180.00 $600.00 08-Oct-01 - 08-Oct-31 Expenses:Books $200.00 $800.00 08-Nov-01 - 08-Nov-30 Expenses:Books $220.00 $1020.00 08-Dec-01 - 08-Dec-31 Expenses:Books $240.00 $1260.00 09-Jun-01 - 09-Jun-30 Expenses:Books $120.00 $1380.00 09-Jul-01 - 09-Jul-31 Expenses:Books $140.00 $1520.00 09-Aug-01 - 09-Aug-31 Expenses:Books $160.00 $1680.00 09-Sep-01 - 09-Sep-30 Expenses:Books $180.00 $1860.00 09-Oct-01 - 09-Oct-31 Expenses:Books $200.00 $2060.00 09-Nov-01 - 09-Nov-30 Expenses:Books $220.00 $2280.00 09-Dec-01 - 09-Dec-31 Expenses:Books $240.00 $2520.00 end test test reg books --monthly --display='amount > $100' 08-Jun-01 - 08-Jun-30 Expenses:Books $120.00 $420.00 08-Jul-01 - 08-Jul-31 Expenses:Books $140.00 $560.00 08-Aug-01 - 08-Aug-31 Expenses:Books $160.00 $720.00 08-Sep-01 - 08-Sep-30 Expenses:Books $180.00 $900.00 08-Oct-01 - 08-Oct-31 Expenses:Books $200.00 $1100.00 08-Nov-01 - 08-Nov-30 Expenses:Books $220.00 $1320.00 08-Dec-01 - 08-Dec-31 Expenses:Books $240.00 $1560.00 09-Jun-01 - 09-Jun-30 Expenses:Books $120.00 $1980.00 09-Jul-01 - 09-Jul-31 Expenses:Books $140.00 $2120.00 09-Aug-01 - 09-Aug-31 Expenses:Books $160.00 $2280.00 09-Sep-01 - 09-Sep-30 Expenses:Books $180.00 $2460.00 09-Oct-01 - 09-Oct-31 Expenses:Books $200.00 $2660.00 09-Nov-01 - 09-Nov-30 Expenses:Books $220.00 $2880.00 09-Dec-01 - 09-Dec-31 Expenses:Books $240.00 $3120.00 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-output.test����������������������������������������������������������0000664�0000000�0000000�00000000646�14411236400�0020564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --output=/dev/stderr __ERROR__ 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-pager.test�����������������������������������������������������������0000664�0000000�0000000�00000000623�14411236400�0020315�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --pager=cat 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test �������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-payee-as-account.test������������������������������������������������0000664�0000000�0000000�00000002333�14411236400�0022355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * (100) January Expenses:Books $10.00 Assets:Cash 2008/01/31 (101) End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 * (102) February Expenses:Books $20.00 Assets:Cash 2008/02/28 (103) End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 * March Expenses:Books $30.00 Assets:Cash test reg --account=payee 08-Jan-01 January January:Expenses:Books $10.00 $10.00 08-Jan-01 January January:Assets:Cash $-10.00 0 08-Jan-31 End of January End of:Expenses:Books $10.00 $10.00 08-Jan-31 End of January End of Jan:Assets:Cash $-10.00 0 08-Feb-01 February Februar:Expenses:Books $20.00 $20.00 08-Feb-01 February February:Assets:Cash $-20.00 0 08-Feb-28 End of February End of:Expenses:Books $20.00 $20.00 08-Feb-28 End of February End of Feb:Assets:Cash $-20.00 0 08-Mar-01 March March:Expenses:Books $30.00 $30.00 08-Mar-01 March March:Assets:Cash $-30.00 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-payee-width.test�����������������������������������������������������0000664�0000000�0000000�00000000727�14411236400�0021444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --payee-width=40 07-Feb-02 RD VMMXX As:Investm:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Dividen:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test �����������������������������������������ledger-3.3.2/test/baseline/opt-payee.test�����������������������������������������������������������0000664�0000000�0000000�00000000660�14411236400�0020323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --payee='account_base + ":" + commodity' 07-Feb-02 VMMXX:VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX 07-Feb-02 VMMXX:$ In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ��������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-pedantic.test��������������������������������������������������������0000664�0000000�0000000�00000000734�14411236400�0021011�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-20 Test GBP Expenses:Phone 20.00 GBP Assets:Cash 2012-03-21 * Test GBP Expenses:Phone 20.00 GBP Assets:Cash test bal --pedantic -> 2 __ERROR__ While parsing file "$FILE", line 2: While parsing posting: Expenses:Phone 20.00 GBP Error: Unknown account 'Expenses:Phone' While parsing file "$FILE", line 6: While parsing posting: Expenses:Phone 20.00 GBP Error: Unknown account 'Expenses:Phone' end test ������������������������������������ledger-3.3.2/test/baseline/opt-pending.test���������������������������������������������������������0000664�0000000�0000000�00000007472�14411236400�0020654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 ! February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 * March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April * Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April * Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 ! Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 ! Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --pending 08-Feb-01 February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 08-May-01 May Assets:Cash $-50.00 $-50.00 08-May-31 End of May Assets:Cash $-50.00 $-100.00 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-percent.test���������������������������������������������������������0000664�0000000�0000000�00000005467�14411236400�0020672�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/11 LIAT Expenses:Travel:Airfare $40.00 Liabilities:MasterCard 2008/01/14 cheaptickets.com Expenses:Travel:Airfare $182.19 Liabilities:MasterCard 2008/02/05 CTX Expenses:Travel:Auto $240.38 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/08/08 BCIS I-131 FILING FEE- Expenses:Travel:Passport $170.00 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/22 AGNT FEE Expenses:Travel:Airfare $70.00 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 LIAT 1974 LIMITED Expenses:Travel:Airfare $418.34 Liabilities:MasterCard 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking test bal --percent 100.00% Assets:Checking 100.00% Expenses:Travel 92.15% Airfare 3.13% Auto 4.72% Passport 100.00% Liabilities:MasterCard end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-period.test����������������������������������������������������������0000664�0000000�0000000�00000017247�14411236400�0020513�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash test reg -p "january 2008" 08-Jan-01 January Expenses:Books $10.00 $10.00 Liabilities:Cards $10.00 $20.00 Assets:Cash $-20.00 0 08-Jan-31 End of January Expenses:Books $10.00 $10.00 Liabilities:Cards $10.00 $20.00 Assets:Cash $-20.00 0 end test test reg -p "monthly january 2008" 08-Jan-01 - 08-Jan-31 Assets:Cash $-40.00 $-40.00 Expenses:Books $20.00 $-20.00 Liabilities:Cards $20.00 0 end test test reg -p "weekly january 2008" 08-Jan-01 - 08-Jan-05 Assets:Cash $-20.00 $-20.00 Expenses:Books $10.00 $-10.00 Liabilities:Cards $10.00 0 08-Jan-27 - 08-Jan-31 Assets:Cash $-20.00 $-20.00 Expenses:Books $10.00 $-10.00 Liabilities:Cards $10.00 0 end test test reg -p "yearly 2008" 08-Jan-01 - 08-Dec-31 Assets:Cash $-3120.00 $-3120.00 Expenses:Books $1560.00 $-1560.00 Liabilities:Cards $1560.00 0 end test test reg -p "from 2009/11/01" 09-Nov-01 November Expenses:Books $110.00 $110.00 Liabilities:Cards $110.00 $220.00 Assets:Cash $-220.00 0 09-Nov-30 End of November Expenses:Books $110.00 $110.00 Liabilities:Cards $110.00 $220.00 Assets:Cash $-220.00 0 09-Dec-01 December Expenses:Books $120.00 $120.00 Liabilities:Cards $120.00 $240.00 Assets:Cash $-240.00 0 09-Dec-31 End of December Expenses:Books $120.00 $120.00 Liabilities:Cards $120.00 $240.00 Assets:Cash $-240.00 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-permissive.test������������������������������������������������������0000664�0000000�0000000�00000000645�14411236400�0021411�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ; The option --permissive quiets balance assertions 2014-05-01 * Opening balance Assets:Cash $100 Equity:Opening balance 2014-05-10 * Spend money Expenses:Foo $10 Assets:Cash -$10 = $80 test bal --permissive $90 Assets:Cash $-100 Equity:Opening balance $10 Expenses:Foo -------------------- 0 end test �������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-pivot.test�����������������������������������������������������������0000664�0000000�0000000�00000011376�14411236400�0020367�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2014-01-01 * Opening balance Assets:Cash 25.00 GBP Equity:Opening balance -25.00 GBP 2014-05-01 * Sell to customer AAA ; Customer: AAA ; Invoice: 101 Assets:Receivables 10.00 GBP Income:Sale -10.00 GBP 2014-05-02 * Sell to customer BBB ; Customer: BBB ; Invoice: 102 Assets:Receivables 11.00 GBP Income:Sale -11.00 GBP 2014-05-03 * Sell to customer AAA ; Customer: AAA ; Invoice: 103 Assets:Receivables 12.00 GBP Income:Sale -12.00 GBP 2014-05-04 * Sell to customer CCC ; Customer: CCC ; Invoice: 104 Assets:Receivables 15.00 GBP Income:Sale -15.00 GBP 2014-05-05 * Money received from customer AAA for invoice 101 ; Customer: AAA ; Invoice: 101 Assets:Cash 10.00 GBP Assets:Receivables -10.00 GBP 2014-05-05 * Sell to customer DDD ; Customer: DDD ; Invoice: 105 Assets:Receivables 20.00 GBP Income:Sale -20.00 GBP 2014-05-07 * Money received from customer CCC for invoice 104 ; Customer: CCC ; Invoice: 104 Assets:Cash 15.00 GBP Assets:Receivables -15.00 GBP 2014-05-08 * Partial payment received from customer DDD for invoice 105 ; Customer: DDD ; Invoice: 105 Assets:Cash 15.00 GBP Assets:Receivables -15.00 GBP test bal assets:receivables --pivot Invoice 28.00 GBP Invoice 11.00 GBP 102:Assets:Receivables 12.00 GBP 103:Assets:Receivables 5.00 GBP 105:Assets:Receivables -------------------- 28.00 GBP end test test bal assets:receivables --pivot Invoice --flat 11.00 GBP Invoice:102:Assets:Receivables 12.00 GBP Invoice:103:Assets:Receivables 5.00 GBP Invoice:105:Assets:Receivables -------------------- 28.00 GBP end test test bal assets:receivables --pivot Invoice --empty 28.00 GBP Invoice 0 101:Assets:Receivables 11.00 GBP 102:Assets:Receivables 12.00 GBP 103:Assets:Receivables 0 104:Assets:Receivables 5.00 GBP 105:Assets:Receivables -------------------- 28.00 GBP end test test bal assets:receivables --pivot Invoice --empty -p "until 2014-05-05" 48.00 GBP Invoice 10.00 GBP 101:Assets:Receivables 11.00 GBP 102:Assets:Receivables 12.00 GBP 103:Assets:Receivables 15.00 GBP 104:Assets:Receivables -------------------- 48.00 GBP end test test bal assets:receivables --pivot Invoice --empty -p "until 2014-05-06" 58.00 GBP Invoice 0 101:Assets:Receivables 11.00 GBP 102:Assets:Receivables 12.00 GBP 103:Assets:Receivables 15.00 GBP 104:Assets:Receivables 20.00 GBP 105:Assets:Receivables -------------------- 58.00 GBP end test test bal assets:receivables --pivot Customer 28.00 GBP Customer 12.00 GBP AAA:Assets:Receivables 11.00 GBP BBB:Assets:Receivables 5.00 GBP DDD:Assets:Receivables -------------------- 28.00 GBP end test test bal assets:receivables --pivot Customer --flat 12.00 GBP Customer:AAA:Assets:Receivables 11.00 GBP Customer:BBB:Assets:Receivables 5.00 GBP Customer:DDD:Assets:Receivables -------------------- 28.00 GBP end test test bal assets:receivables --pivot Customer --empty 28.00 GBP Customer 12.00 GBP AAA:Assets:Receivables 11.00 GBP BBB:Assets:Receivables 0 CCC:Assets:Receivables 5.00 GBP DDD:Assets:Receivables -------------------- 28.00 GBP end test test bal assets:receivables --pivot Customer --empty -p "until 2014-05-05" 48.00 GBP Customer 22.00 GBP AAA:Assets:Receivables 11.00 GBP BBB:Assets:Receivables 15.00 GBP CCC:Assets:Receivables -------------------- 48.00 GBP end test test bal assets:receivables --pivot Customer --empty -p "until 2014-05-06" 58.00 GBP Customer 12.00 GBP AAA:Assets:Receivables 11.00 GBP BBB:Assets:Receivables 15.00 GBP CCC:Assets:Receivables 20.00 GBP DDD:Assets:Receivables -------------------- 58.00 GBP end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-plot-amount-format.test����������������������������������������������0000664�0000000�0000000�00000000441�14411236400�0022762�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg -j --plot-amount-format='X %(format_date(date, "%Y-%m-%d")) Y %(quantity(scrub(display_amount)))\n' X 2007-02-02 Y 0.35 X 2007-02-02 Y -0.35 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-plot-total-format.test�����������������������������������������������0000664�0000000�0000000�00000000440�14411236400�0022601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg -J --plot-total-format='X %(format_date(date, "%Y-%m-%d")) Y %(quantity(scrub(display_amount)))\n' X 2007-02-02 Y 0.35 X 2007-02-02 Y -0.35 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-prepend-format.test��������������������������������������������������0000664�0000000�0000000�00000001453�14411236400�0022144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --prepend-format "%(account_base)" VMMXX 0.350 VMMXX Assets:Investments:Vanguard:VMMXX VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- $-0.35 0.350 VMMXX end test test reg --prepend-format "%(account_base)" VMMXX07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test test accounts --prepend-format "%(account_base) " VMMXX Assets:Investments:Vanguard:VMMXX VMMXX Income:Dividends:Vanguard:VMMXX end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-prepend-width.test���������������������������������������������������0000664�0000000�0000000�00000001652�14411236400�0021774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal --prepend-format "%(account_base) " --prepend-width=10 VMMXX 0.350 VMMXX Assets:Investments:Vanguard:VMMXX VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- $-0.35 0.350 VMMXX end test test reg --prepend-format "%(account_base) " --prepend-width=10 VMMXX 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test test accounts --prepend-format "%(account_base) " --prepend-width=10 VMMXX Assets:Investments:Vanguard:VMMXX VMMXX Income:Dividends:Vanguard:VMMXX end test ��������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-price-db.dat���������������������������������������������������������0000664�0000000�0000000�00000000100�14411236400�0020463�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������P 2012-03-16 06:47:12 CAD $2.50 P 2012-03-17 06:47:12 CAD $3.50 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-price-db.test��������������������������������������������������������0000664�0000000�0000000�00000000306�14411236400�0020702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-17 KFC Expenses:Food 20 CAD Assets:Cash test pricedb --price-db test/baseline/opt-price-db.dat P 2012/03/16 06:47:12 CAD $2.5 P 2012/03/17 06:47:12 CAD $3.5 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-price.test�����������������������������������������������������������0000664�0000000�0000000�00000003324�14411236400�0020322�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * Purchase Apple shares Equities 1000 AAPL @ $2 Cash $-2000 2008/06/30 * Sell some Apple shares Cash $1250 Equities -500 AAPL {$2} @ $2.50 Income:Gains $-250 P 2008/10/01 02:18:02 AAPL $3 P 2009/01/31 02:18:02 AAPL $4 P 3000/01/01 02:18:02 APPL $100 test reg equities 08-Jan-01 Purchase Apple shares Equities 1000 AAPL 1000 AAPL 08-Jun-30 Sell some Apple sha.. Equities -500 AAPL 500 AAPL end test test reg -B equities 08-Jan-01 Purchase Apple shares Equities $2000 $2000 08-Jun-30 Sell some Apple sha.. Equities $-1000 $1000 end test test reg --end 2009/06/26 -V equities 08-Jan-01 Purchase Apple shares Equities $2000 $2000 08-Jun-30 Commodities revalued <Revalued> $500 $2500 08-Jun-30 Sell some Apple sha.. Equities $-1250 $1250 09-Jan-31 Commodities revalued <Revalued> $250 $1500 09-Jun-26 Commodities revalued <Revalued> $500 $2000 end test test reg --end 2009/06/26 -G equities 08-Jun-30 Commodities revalued <Revalued> $500 $500 08-Jun-30 Sell some Apple sha.. Equities $-250 $250 09-Jan-31 Commodities revalued <Revalued> $250 $500 09-Jun-26 Commodities revalued <Revalued> $500 $1000 end test test reg -I equities 08-Jan-01 Purchase Apple shares Equities $2000 $2000 08-Jun-30 Sell some Apple sha.. Equities $-1000 $1000 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-pricedb-format.test��������������������������������������������������0000664�0000000�0000000�00000000652�14411236400�0022117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D $1,000.00 P 2009/01/01 13:30:00 AAPL $10.00 P 2009/01/01 14:30:00 AAPL $20.00 P 2009/01/01 15:30:00 AAPL $30.00 P 2009/01/01 16:30:00 AAPL $40.00 P 2009/02/01 17:30:00 AAPL $50.00 2009/03/01 Purchase Assets:Brokerage 100 AAPL Income test pricedb --pricedb-format='P %(date) %(scrub(display_amount))\n' P 2009/01/01 $10.00 P 2009/01/01 $20.00 P 2009/01/01 $30.00 P 2009/01/01 $40.00 P 2009/02/01 $50.00 end test ��������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-prices-format.test���������������������������������������������������0000664�0000000�0000000�00000000715�14411236400�0021774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D $1,000.00 P 2009/01/01 13:30:00 AAPL $10.00 P 2009/01/01 14:30:00 AAPL $20.00 P 2009/01/01 15:30:00 AAPL $30.00 P 2009/01/01 16:30:00 AAPL $40.00 P 2009/02/01 17:30:00 AAPL $50.00 2009/03/01 Purchase Assets:Brokerage 100 AAPL Income test prices --prices-format='%(datetime) %(scrub(display_amount))\n' 2009/01/01 13:30:00 $10.00 2009/01/01 14:30:00 $20.00 2009/01/01 15:30:00 $30.00 2009/01/01 16:30:00 $40.00 2009/02/01 17:30:00 $50.00 end test ���������������������������������������������������ledger-3.3.2/test/baseline/opt-primary-date.test����������������������������������������������������0000664�0000000�0000000�00000011015�14411236400�0021612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; primary-date display primary dates for all calculations 2014/01/01=2014/01/13 Client invoice ; estimated date you'll be paid Assets:Accounts Receivable $100.00 Income: Client ABC 2014/01/01=2014/01/15 Client invoice ; actual date money received Assets:Accounts Receivable $100.00 Income: Client ABC ; will not affect checking account 2013/10/16 * (2090) Bountiful Blessings Farm Expenses:Food:Groceries $ 37.50 ; [=2013/10/01] Expenses:Food:Groceries $ 37.50 ; [=2013/11/01] Expenses:Food:Groceries $ 37.50 ; [=2013/12/01] Expenses:Food:Groceries $ 37.50 ; [=2014/01/01] Expenses:Food:Groceries $ 37.50 ; [=2014/02/01] Expenses:Food:Groceries $ 37.50 ; [=2014/03/01] Assets:Checking test bal Income --begin 2014/01/01 --end 2014/01/14 $ -200.00 Income: Client ABC end test test bal Income --effective --begin 2014/01/01 --end 2014/01/14 $ -100.00 Income: Client ABC end test test bal Income --primary-date --effective --begin 2014/01/01 --end 2014/01/14 $ -200.00 Income: Client ABC end test test bal Income --actual-dates --effective --begin 2014/01/01 --end 2014/01/14 $ -200.00 Income: Client ABC end test test reg Income --begin 2014/01/01 --end 2014/01/14 14-Jan-01 Client invoice Income: Client ABC $ -100.00 $ -100.00 14-Jan-01 Client invoice Income: Client ABC $ -100.00 $ -200.00 end test test reg Income --effective --begin 2014/01/01 --end 2014/01/14 14-Jan-13 Client invoice Income: Client ABC $ -100.00 $ -100.00 end test test reg Income --primary-date --effective --begin 2014/01/01 --end 2014/01/14 14-Jan-01 Client invoice Income: Client ABC $ -100.00 $ -100.00 14-Jan-01 Client invoice Income: Client ABC $ -100.00 $ -200.00 end test test reg Income --actual-dates --effective --begin 2014/01/01 --end 2014/01/14 14-Jan-01 Client invoice Income: Client ABC $ -100.00 $ -100.00 14-Jan-01 Client invoice Income: Client ABC $ -100.00 $ -200.00 end test test reg checking 13-Oct-16 Bountiful Blessings.. Assets:Checking $ -225.00 $ -225.00 end test test reg checking --primary-date --effective 13-Oct-16 Bountiful Blessings.. Assets:Checking $ -225.00 $ -225.00 end test test register Groceries 13-Oct-16 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 37.50 Expense:Food:Groceries $ 37.50 $ 75.00 Expense:Food:Groceries $ 37.50 $ 112.50 Expense:Food:Groceries $ 37.50 $ 150.00 Expense:Food:Groceries $ 37.50 $ 187.50 Expense:Food:Groceries $ 37.50 $ 225.00 end test test register Groceries --effective 13-Oct-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 37.50 13-Nov-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 75.00 13-Dec-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 112.50 14-Jan-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 150.00 14-Feb-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 187.50 14-Mar-01 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 225.00 end test test register Groceries --primary-date --effective 13-Oct-16 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 37.50 Expense:Food:Groceries $ 37.50 $ 75.00 Expense:Food:Groceries $ 37.50 $ 112.50 Expense:Food:Groceries $ 37.50 $ 150.00 Expense:Food:Groceries $ 37.50 $ 187.50 Expense:Food:Groceries $ 37.50 $ 225.00 end test test register Groceries --actual-dates --effective 13-Oct-16 Bountiful Blessings.. Expense:Food:Groceries $ 37.50 $ 37.50 Expense:Food:Groceries $ 37.50 $ 75.00 Expense:Food:Groceries $ 37.50 $ 112.50 Expense:Food:Groceries $ 37.50 $ 150.00 Expense:Food:Groceries $ 37.50 $ 187.50 Expense:Food:Groceries $ 37.50 $ 225.00 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-quantity.test��������������������������������������������������������0000664�0000000�0000000�00000000622�14411236400�0021074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --quantity 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ��������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-quarterly.test�������������������������������������������������������0000664�0000000�0000000�00000010174�14411236400�0021251�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --quarterly books 08-Jan-01 - 08-Mar-31 Expenses:Books $120.00 $120.00 08-Apr-01 - 08-Jun-30 Expenses:Books $300.00 $420.00 08-Jul-01 - 08-Sep-30 Expenses:Books $480.00 $900.00 08-Oct-01 - 08-Dec-31 Expenses:Books $660.00 $1560.00 09-Jan-01 - 09-Mar-31 Expenses:Books $120.00 $1680.00 09-Apr-01 - 09-Jun-30 Expenses:Books $300.00 $1980.00 09-Jul-01 - 09-Sep-30 Expenses:Books $480.00 $2460.00 09-Oct-01 - 09-Dec-31 Expenses:Books $660.00 $3120.00 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-raw.test�������������������������������������������������������������0000664�0000000�0000000�00000000511�14411236400�0020004�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test print --raw 2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-real.test������������������������������������������������������������0000664�0000000�0000000�00000000445�14411236400�0020144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 (Liabilities:Cards) $10.00 Assets:Cash test reg --real 08-Jan-01 January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-recursive-aliases.test�����������������������������������������������0000664�0000000�0000000�00000000420�14411236400�0022640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������alias A=B:A alias B=C:B alias C=D:C 2001-01-01 Test A 10 EUR Foo test reg --recursive-aliases 01-Jan-01 Test D:C:B:A 10 EUR 10 EUR Foo -10 EUR 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-register-format.test�������������������������������������������������0000664�0000000�0000000�00000000334�14411236400�0022330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --register-format='%(amount)\n' 0.350 VMMXX {$1.00} [2007/02/02] $-0.35 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-related-all.test�����������������������������������������������������0000664�0000000�0000000�00000001025�14411236400�0021402�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/06/18 This is a Test Expenses:Food $20.00 Expenses:Tips $2.00 Expenses:Tax $3.00 Liabilities:Credit test reg --related-all credit 09-Jun-18 This is a Test Expenses:Food $20.00 $20.00 Expenses:Tips $2.00 $22.00 Expenses:Tax $3.00 $25.00 Liabilities:Credit $-25.00 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-related.test���������������������������������������������������������0000664�0000000�0000000�00000000700�14411236400�0020633�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/06/18 This is a Test Expenses:Food $20.00 Expenses:Tips $2.00 Expenses:Tax $3.00 Liabilities:Credit test reg --related credit 09-Jun-18 This is a Test Expenses:Food $20.00 $20.00 Expenses:Tips $2.00 $22.00 Expenses:Tax $3.00 $25.00 end test ����������������������������������������������������������������ledger-3.3.2/test/baseline/opt-revalued-only.test���������������������������������������������������0000664�0000000�0000000�00000003317�14411236400�0022010�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/01/01 Sample 1a Assets:Brokerage:Stocks 100 S Assets:Brokerage:Cash -100 P P 2009/01/01 00:00:00 S 2 P 2009/02/01 Sample 2a Assets:Brokerage:Stocks 100 S @ 1 P Assets:Brokerage:Cash P 2009/02/01 00:00:00 S 4 P 2009/03/01 Sample 3a Assets:Brokerage:Stocks 100 S @@ 100 P Assets:Brokerage:Cash P 2009/03/01 00:00:00 S 8 P 2009/04/01 Sample 4a Assets:Brokerage:Cash 100 P Assets:Brokerage:Stocks -100 S {1 P} P 2009/04/01 00:00:00 S 16 P ; In this usage case, the top amount is always secondary ; 2010/01/01 Sample 1b ; Assets:Brokerage:Cash -100 P ; Assets:Brokerage:Stocks 100 S ; ; P 2010/01/01 00:00:00 S 2 P 2010/02/01 Sample 2b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @ 1 P P 2010/02/01 00:00:00 S 4 P 2010/03/01 Sample 3b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @@ 100 P P 2010/03/01 00:00:00 S 8 P 2010/04/01 Sample 4b Assets:Brokerage:Stocks -100 S {1 P} Assets:Brokerage:Cash 100 P P 2010/04/01 00:00:00 S 16 P test reg --market --revalued-only stocks 09-Feb-01 Commodities revalued <Revalued> 200 P 400 P 09-Mar-01 Commodities revalued <Revalued> 800 P 1600 P 09-Apr-01 Commodities revalued <Revalued> 2400 P 4800 P 10-Feb-01 Commodities revalued <Revalued> -2400 P 800 P 10-Mar-01 Commodities revalued <Revalued> 1200 P 2400 P 10-Apr-01 Commodities revalued <Revalued> 3200 P 6400 P end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-revalued.test��������������������������������������������������������0000664�0000000�0000000�00000004401�14411236400�0021024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/01/01 Sample 1a Assets:Brokerage:Stocks 100 S Assets:Brokerage:Cash -100 P P 2009/01/01 00:00:00 S 2 P 2009/02/01 Sample 2a Assets:Brokerage:Stocks 100 S @ 1 P Assets:Brokerage:Cash P 2009/02/01 00:00:00 S 4 P 2009/03/01 Sample 3a Assets:Brokerage:Stocks 100 S @@ 100 P Assets:Brokerage:Cash P 2009/03/01 00:00:00 S 8 P 2009/04/01 Sample 4a Assets:Brokerage:Cash 100 P Assets:Brokerage:Stocks -100 S {1 P} P 2009/04/01 00:00:00 S 16 P ; In this usage case, the top amount is always secondary ; 2010/01/01 Sample 1b ; Assets:Brokerage:Cash -100 P ; Assets:Brokerage:Stocks 100 S ; ; P 2010/01/01 00:00:00 S 2 P 2010/02/01 Sample 2b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @ 1 P P 2010/02/01 00:00:00 S 4 P 2010/03/01 Sample 3b Assets:Brokerage:Cash Assets:Brokerage:Stocks 100 S @@ 100 P P 2010/03/01 00:00:00 S 8 P 2010/04/01 Sample 4b Assets:Brokerage:Stocks -100 S {1 P} Assets:Brokerage:Cash 100 P P 2010/04/01 00:00:00 S 16 P test reg --market --revalued stocks 09-Jan-01 Sample 1a Asset:Brokerage:Stocks 200 P 200 P 09-Feb-01 Commodities revalued <Revalued> 200 P 400 P 09-Feb-01 Sample 2a Asset:Brokerage:Stocks 400 P 800 P 09-Mar-01 Commodities revalued <Revalued> 800 P 1600 P 09-Mar-01 Sample 3a Asset:Brokerage:Stocks 800 P 2400 P 09-Apr-01 Commodities revalued <Revalued> 2400 P 4800 P 09-Apr-01 Sample 4a Asset:Brokerage:Stocks -1600 P 3200 P 10-Feb-01 Commodities revalued <Revalued> -2400 P 800 P 10-Feb-01 Sample 2b Asset:Brokerage:Stocks 400 P 1200 P 10-Mar-01 Commodities revalued <Revalued> 1200 P 2400 P 10-Mar-01 Sample 3b Asset:Brokerage:Stocks 800 P 3200 P 10-Apr-01 Commodities revalued <Revalued> 3200 P 6400 P 10-Apr-01 Sample 4b Asset:Brokerage:Stocks -1600 P 4800 P end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-rich-data.test�������������������������������������������������������0000664�0000000�0000000�00000002660�14411236400�0021056�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test -f /dev/null convert test/baseline/feat-convert-with-directives.dat --now '2014/08/01' 2012/01/01 * KFC Expenses:Unknown $10 Equity:Unknown 2012/01/02 * REWE SAGT DANKE 123454321 Expenses:Unknown 10€ Equity:Unknown end test test -f /dev/null convert test/baseline/feat-convert-with-directives.dat --detail --now '2014/08/01' 2012/01/01 * KFC ; CSV: 2012/01/01,KFC,$10 ; Imported: 2014/08/01 ; UUID: 4352cc5a03f882f6f159b90a518667bde7200351 Expenses:Unknown $10 Equity:Unknown 2012/01/02 * REWE SAGT DANKE 123454321 ; CSV: 2012/01/02,"REWE SAGT DANKE 123454321",10€ ; Imported: 2014/08/01 ; UUID: 4d04439fba0c7336377d1191c545efd0cfa15437 Expenses:Unknown 10€ Equity:Unknown end test test -f /dev/null convert test/baseline/feat-convert-with-directives.dat --rich-data --date-format %d-%m-%Y --now '2014/08/01' 01-01-2012 * KFC ; CSV: 2012/01/01,KFC,$10 ; Imported: 01-08-2014 ; UUID: 4352cc5a03f882f6f159b90a518667bde7200351 Expenses:Unknown $10 Equity:Unknown 02-01-2012 * REWE SAGT DANKE 123454321 ; CSV: 2012/01/02,"REWE SAGT DANKE 123454321",10€ ; Imported: 01-08-2014 ; UUID: 4d04439fba0c7336377d1191c545efd0cfa15437 Expenses:Unknown 10€ Equity:Unknown end test ��������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-script.dat�����������������������������������������������������������0000664�0000000�0000000�00000000126�14411236400�0020312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--no-pager --columns=80 bal --no-pager --columns=80 reg --no-pager --columns=80 print ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-script.test����������������������������������������������������������0000664�0000000�0000000�00000000742�14411236400�0020525�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-17 KFC Expenses:Food 20 CAD Assets:Cash test --script test/baseline/opt-script.dat -20 CAD Assets:Cash 20 CAD Expenses:Food -------------------- 0 12-Mar-17 KFC Expenses:Food 20 CAD 20 CAD Assets:Cash -20 CAD 0 2012/03/17 KFC Expenses:Food 20 CAD Assets:Cash end test ������������������������������ledger-3.3.2/test/baseline/opt-seed.test.save�������������������������������������������������������0000664�0000000�0000000�00000047152�14411236400�0021104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������generate --seed=10 <<< >>>1 2186/12/04=1987/04/07 (gxB) Xcm0wJS7fGh07 ; K93x8n5028OV19:n9zv0ksC [MIdo:Y377U9xhRjxQaFFk37:aEaw66xn:9ss914] 411126pQ @ ozgYxo0.578679 ; n1:1Bm38PMy6FO51R58pWhXmc1HN08Umw8T (X:S) -936200r @ nijv235713 ; j7zUn1U8UPStcn46 72dpS5vd9d2oxQk:R C:27lps3E 2186/12/09=1987/04/08 (B7xDea) yA6fOo:q0A6IaA7v ; LB:l cro FhdXRF:nROGw1H8PqY10Jg 3FQRrcX07 -733399YmHWf @ 0.640803LMtLP ; dVmRkOR [Z2t:31I:2ox:81NC28A0] 263844DKa ; h4I8M355 B0VD1MI2073MblRrsPV64O9 EEMQ 816045 @ 0.499731hzxnl ; qAsYFCY [b1X09LHz:AkK5G] U -785951 ; gJy3hZ4g75vf84:9Rz R 6Vpy11ZJ:24K m U:Y DI7 3 ; H64 2186/12/11=1987/04/12 ! (Xu:Eo) Cx4o2an 3Upa0Fll:nDjT461:y ; uzZ5uQlLWR4 [XKU2Jj6:p6I5kVSPv 3vpG:ShyKRF:dv8y] Tgojw -14862.9 [SPOiCDS5Zg:t:Q7vMF01n8P:7h1i] qXxf 437098 @ BmJ866903 [reGXB0LK38iD5FM zWxl PUW:eI5F21EN80e] acLq947715 (NA LA 11uhxP8y7oBBgXZQ45lwPb1) 14641.5sndK @ 44.2184885 Cw ; P4158:i1xa1eoHUHeF1pS8 [CMs:8Yz9t7H1aF3qN] qt250589 (B0T) a-715047 @ 0.946357 Ac ; PeUU8DYH3 BxS Uu9y:YfU0isX6ly:sZO6S2xpN V:60vuV 2186/12/12=1987/04/14 * (OEWOu) SLGS ; AuFLS1XOW:z04:gwQ2 (VjI Tm79d6) -93666.5xuIH ; A9 [UF3xXu kOA ERwiQOh aprUp9gM] -103361jSZeo @ 564002 iJOXN (EzJ 65T295) QCaR134452 @ RuLEEw 0.6292982 ; M8916aJx5C7:G9U0I:y4t68 W oUJuq66YfULC1 CW95nX2VZch2:qK992pM3fhy manJj -209990 @ c 536098 ; yzOvW 28W99zgmW9Y9:j:5w:1:gIX rNUT0rM:rBsW:P87ZVn3d8X 2186/12/17=1987/04/15 * (F7u) sZ:c vZ8U3Sr705 T2DT75mS9x7 W727197 @ 1.264289dfiel (Z X9E8W16j8:521) zPFS-334854 ; RkIZ27O30b8:yf0pXU (jAmK) 426564AO @ SemS 421251 ER lq6N3 q 3317.11 @ RAAii 254.77207569 ixS 2186/12/21 * (EVhm) fnA 7GRR710:8:FhSlZRc:4L5J74Q RnH9H4v ; N2 (dGg77g:i40RE7J3KnxNNHB0E 7) 262042 z ; N3V98V827I:qaJ2U4E5IR tpHJw0J699:f M8:vCvoagZmVL1Y:543:eucH98:Q 222936 aZtSl @ 1.851056rQWi ; Os2Z7rnOU11Y9vm8pGNvTuK [KWybHT:s] Zc -395512 @ 673430 Q ; JDK FpB 6g 5dBDa [Tk:B1257JrD9:LgqbzmH8GQ] UaS-222545 ; uiM6 u335Ic 2186/12/27 ! (GS) EJ99iD7cHek7EJp5aoFYIXz2Wx:hTSIPWjP [rRlyn:21:Jcu292Ea] YuOJ-77111.5 @ ys9.708928 ; EI309:2D t66H6NzNv c AXmMzeF4:kR [jn 58DWi8DwD48Ee3:05qkh38Y8754VMaC80OD] 205960LdQ @ iiB 2.206851 ; vTJ9I oLQ9KF1l [zqBE2yO1UWmgM4BL:A9Do4EffRd8V:io] w-53161.6 @ 350764 UclkLw g:R000qI3C 746879 juFf @ hlN448555 ; x5:1OGWm WY9Fdtu:8rD09k1:5z 1:8Y:xQ11I A43J7Yjx6xKhN0:TjP ; f:l8Q 1L7RjfBQ 2186/12/29 * (R3:UJ) hN K3YTd:0y:42Ze9ObAaT lgx:yJ:ir1j ; xIIL05IE2p3 [y jLigz uChQA Eg4UEf0 70B:94:i4DqcQq] iyg895724 @ 0.510892 mCiSp w8C 7Hx8Rq91QS6z:7b -331711 vDTIl U0987:F:4DTh6V F6 Rkp3p1 CTDATK-782095 [kG7R6F:9jPxChWVOObKg045AE80hc87FW] 252408ViNkjs jHM I5:Y 2187/01/02=1987/04/20 (oCi) coD:3Y1ZEF79r:C086 ; q:Gk:331:S9 o:0CREm (bML5K:jl7ntsH213:Q:1BjWS8Lrqp:3 qSg2 93) SgXoC 338602 [mr7R wS405TMYlnq7:Jx:e7:Qv8uElrM01Ww] 596325 IEVnen ; V220WkzjO p:eVRrzBl473Nht:26U o1h:E ; n:34:ThQmLaTUfgbrNm:71p1C9 2187/01/03=1987/04/24 * (e) S:8O8F8P4672h6 ; UEJwkn (iA:R70:9:Wzp4x:v9skm) SXV 822239 o6Vo3X:HmQ:8s94RFZ8:fbxJYY2:xy6 wE 129507 @ 7.102689 hq ; T:Dr0SwdlwU:3gn49 TDM8vS3 4:l497yk2f2 d307311 @ 0.344104 xOqmt ; hrZn9 5BaTBOkT16 y:65f848liI558:f9 ADHTI7b:417f9NC p:81Dn:dd:2t58QPvkr6 280677 ewaJgw @ OhuZ248193 ; US178U5:E2P JvY8mjt0 p1F ; Er nO 2187/01/09 (u) I55B7BN7Nic0Xf7l:enK6:wZEp [u7n1K7hy3kMKbPG hL] pIYM -959467 [Xe3P2P:oUoMA1slU09R] -544505SfH @ 852228 OkXknF ; z uE9:xQ53Sy0 s3p53voVFV:bOP2mL9K4hBf:24 YJ5N:el1lEP1rksmQ4af8vXzlix:87 oXtoLV-494724 [f28 6:EG5sn 6clN E:689EhZ6rljIS9A05HB] -592178TcaIqX @ 0.52632KqgxJY oiMz51lFxbnN 2187/01/10 * (f:YKS) b152q ; l0728x5Ep97f405oy5sTr4j (X2l:yjc3P5Jf0R8slv7fn:DWa0HB63yUBY) -637008UW b -526513qGuvC (P9QJnILJLlK20j9) VJx -237745 ; H0:S1m6o:kpqGBmr0jn4yGp0PPx63680Bo [JpVMi82HvZ8Cjn8R] jVSSg-913619 @ 0.808966 yoc ; jYk3X58298IFBV7M TH3WN4n l0:x73agt4z (HU 0M:e3A:030XdN3:8g) 243401kRXr @ eW 346761 ; V6tnx1GMt6B0iT5DL9 Eh EjQL:5Rj:68NZ42kd 16djH k883779 @ 652143 KQK hlxM319I2412y 2187/01/14 (p7NK) i56 (E0V bMA) -901145n ; I0G5G9axvs32G0R19D72Nj c:6q9 dM:73:5Nc1p5g:c0 q -764271.00 @ 0.310259Pwv [Nf2:jSoF:X79mOgTa:t4 CC8903Ug 0cf Gv4w] -345268RzzzmL @ e 1.692647 ; R:KT0Y:WIkiD3UB2Fu6:CK5X6G6K1 (d) PDGe 828999 ; iFk6G5XkFII6739 31Dq2Qde25a:vfvRL0 NDODeT1ud913r9L:s7JDMkkGwNHEoac ; FyMDFsNG 2187/01/20=1987/04/29 ! (uvZ) C79:n5SB4r OV35363j:REM:JjkW -996837ufeCSQ @ 0.47472dq ; EqA:SE2QJpd20a [JTaWorK:018RpRg1y23Eh2FzS:o84BimcBP] 644258 VmNxi uh:l8fPO05YCYi053 2187/01/23 * (M) p4yw23Z7z708H2t 1d108W509F XtQHd ; JrdATcORmZ7F4:RilGAo4nPERSla K8:q319 (pgNXjW:7:w c6434RmZg9x39A QgUoDd8NZ) SUO 203170 [ob] DHAra-72498.6 @ 2.7968954yeCD ; xTxkz:Qo:27 6146k0 [o:1wP7Q2] 77384 dQolH @ 492890 l ; H:d6 Yu [vhjm6FF] -597065 iBP @ brzs0.0717997 B1V:H4agb9:aLP9235e2b 2187/01/29 (KQhu) Q7x65f8ls25W88oyPR1H56I (lX 0:3ZQ8e:f:We0b4lAUvFl7lf5x208) 383456kQrwsT ; wa0f6v8ZJ7:on7T [She:qWK1Qaijt7B5ua0] kniumo499909 ; Z:CP:A5hK5uN4JI7FMqS3V:vyVS2 [Y68R:A5N] 798458a ; x:26gTlXF1:7:R6BMewcn:H:N8UXE (QfdF:4GLhMy) b-547602 ; b aPGwAfp Qx:m952ctx495 [utb8nOU0r9N] mLHbE223101 A6f1R:zEq0Vea3gsKnC9:06 a224 ZmNQ 774115 @ 892757 OQv ; X6g:0Ka:8E735uVV W6zw11QYbiE3awi1d A ; pXFvBnAu8E77qcw:ZUlwkWV5lZ:VkC3:5gZyaERW 2187/02/01=1987/05/01 ! (wv5i) f6n2pmhOP77oyv6:IG5j0TEk9v5pXP9810s V 5w Po:lVghM1g4t3BXKy B B:1Zz gT -123047 S3j:Aj4s 791849I [B9:WfB9KIt4vLU51:Jm99I6Q125dMRIqx66vC5z2] 242497 A @ 583994 uXRfdk ; p5H8:j:o:fES lS7d:q -106717 iefO sj 2187/02/04 (s) Z1yWLuX5yE4h:xT0X0Ga:o7:8T:0BbAvP46 ; X79u5:K mYCw9Utl (uE:h:1jzdLirp r gP5Z:AF:79I) -711627oRDqJP @ CLpS1.184335 ; DUx7WX5j4nX [t2xBfs53KgY vP:XxCk2bmJC92 3Sx5U4] 313034 GINL ; lLpf64qmRAVY7J943jaE:c:l 4570l4:P u8 84Cpd e3 ; g a m05HNW45pto sn0pJPv0U9r5bfT 2187/02/07 (aj:5) X7rkc3PjvxTL l V8aoGZ8p IL 50587.5vBeRJ M6Ai3R:Q 325120 O J5EUo35CK:qrg4E:V RznCQB3:q8MX1tX0CDyp 975643CDIcM @ 855572Twu ; bf5 A [TM5Z] 913463 oimIl YI8S9:X:C6zA 75 2187/02/08=1987/05/03 (u5N2W) X2Lbql6n O:z2U1WB0 iDB8N0:4O286Be4h5q5 [q95j7F] Z -886734 @ 931353 bNt ; u0G KuFLh nq68 (Y:3LOBz1:S:nA7B) wwL-880685 @ 960618G X9:gud7uR5Qxth7:f1Q0 ; F2lUncNAoUU4hWY DVe 2187/02/10 * (E) n07K0X69w9Lm5n:Kas2ErCM6l4 48cR18Y:SnV [R Cv H:6SpHO6:l63Gq:20JXImfc] -667443 y ; Kv [mI9ZfM02qNaD8] 668500 RYSLl @ ddu0.19124 d:BPcI1lYVW9CcA6yL:4lhaK6ID717 ; N8Ix1H95urPX pLL5m:vz1mg:e A8 2187/02/13=1987/05/05 ! (c) Bb7b10:fG8C 6 6Wx1l [Ef] -85775.1dN @ 315212ejCU (wLjq:0601Iqx:96 HF) 658188HKt suu2OqWMY6sJFv:xm:Tx -638358 jnDT [g:9S f8OoiA87l0I4Kqx3] -601167p @ 43014.8 aLq ; S:2T:i:IzbT:bNP5snh:2Pk:5nu lN 8U973P3983pe32X:A:219N:1 ; W:0 BO yy8:w8evC7YmboP ghx2:J9Xo 2187/02/18 * (AN) oUwsLl:0Mm6p66d4leuup0:2x ; c 5kp7U8G1k4:3qemxEP9IIYN8YUxA:8I9q ysc8Prp:BAO3:NmJPIpZli -863446K @ VQ568439 ; tA6 (I 4u8PI1L81v:aOP1QE74d 1DDZBY e7) Xl -571100 ; C3SV V0zIpW0K6O:Uy1d:Q7dvwz:o5B EQ Nh (L bD:5l2K650y8i9Ja) d-942453 ; FjCMw2cXC3T5S1 P7d:d:O63BwR6O (qYLNQ:ApC) P -371484 @ WLbB 716482 ; LkfG:wU7kt 6Ayo9H9 ho (AH7Z266:lo9126ooP:ok6IqMf0) -695058SXInq @ Abz 913865 ; Lqh76O:sSk3Y9zRR [f:z:n2:bO7fwc230R:2Y7fjX9Yqq:649] RkqnLo 249887 @ vTD 1.7248 IW0M187 Ub ; u4h0r2cSYS1kHW 9j6FyRk1y 2187/02/20=1987/05/06 (W95) k [TDrC8nTiD:r4Dx9177sQJvd16Ed9SvU mrE81:oD] bipfJi -629380 [cD2:Hy:DV6zL a647Aq7Pp71gTruf:0] BzPmpg 612835 ; W3FiFcDe53HIm789:kQN (nP6CPmJd9g7jS72M 41q:t V5ltrdUx169Zu28) 975397 shihH qT7c6 -655081 BXPPc @ Ya 872226 ; yIX7OGx (mQSZ) 426510 zcBER @ tsnr606449 ; cJS:z12J4QvOA [Yjfk:FmxjrVAa278WBuv5:50eG7w] Xake56556.8 uM61VJYWHmmS2swhTs1624firiH7MjO5Z5 M0Y 2187/02/23=1987/05/08 ! (R) Dg8:279q:90GKH7:pq3WN kXi:C:4qf76 ; NlnAoFwM C7d3KH8A:6:lHr o8kxh6N130jo:kJ VI6YZ -597872tIjW (e:81C4:oO swE5f 9l:d9) wUna-360797 ; mJ y8mI (a2y 6:iZLCdeW t10vqB7n3YN:7) 272995 NcKRN ; taqP:G6i 8ePbU7GD59:r6iS E46j:t7f50rjzczn -420867In (qfK1A qE9SAMwwY:RbfPDS) lAcJ136457 ; AJ7hu5lhG2Q m9GnlWF:P 2e6300i0o:K 930528Om @ 127927Rq ; YN86XuS17Ax6fBYw8:FZB v89c7V90x898JVnHFV:i28l3mMf1xTU:Bo7iJ2d ; YHqY 2187/02/24=1987/05/12 * (t87o) mjrM4MyN8oKdpS4s ; dh4gFZgBk5z29g2F44zT5I9g58Xpo:AYe9u3 (uQI843zXpD9vK6l2dc93) VDZfau695674 @ 1.395381tJ A:UT:2t 4BPcec8xuFg Qb2sRg79Wt -715837BWwCJu i uRN1hvI8 ; Yh 2:snU9F5u0Q7:eT507nI3mN oSE8MkeZ0kPnF 2187/03/02 (JaP) M:j9:DzCYyd:M V6 ; YCd5tl8gTY7r KWu6Q vju54Wqg:Hajy3K5 699037PsAu @ 0.0432019 dmmsRU gm:9:Uf:t:jZCaY8ZoPqd UB:qjC5OtR v2y -65372.2DRpj @ aPXbpG0.66845387 ; HOntIQNDiUq2h7O88 Lq6 ; hB1 YKJgE pnE0a 2187/03/08=1987/05/16 (fVUT) GB0y15 ; gw8ge764c35f [fgY:5j4fG6:uBT] 994884 IuSb [Qv84kw0f1cxt8MBl] -314694 SKN @ uIGy0.488405 ; S x865af7EOVbh09z1C:atG C:aOmIU 6quv27:L8:fO0X61IFuzPKm ; cU95Tz:wN9F99u2C:7QW2R6QdK:y:5Lcdf 2187/03/11=1987/05/18 * (Q2P) kOT0:08TY r19iHviTP MQmbBklzoe5Zy:8T4 -701612X @ 1.347401 dhCZ j8hn 8a:zJvx -950080X ; X6V4:Vrz7:A9 ChMhD kwW2Drf8oW4 m23b6 rg17K cc 2187/03/16 * (tZ) U C44:BGa4J6Bzwwb4o [YK49QGHo:U167ZuH:Sus] -653997 r @ 990828 WsMH ; YY7Vl4m0y6h:97Eb:t:WZ38 [rkd6eQ QnFRO4Eap1:o24ze7 MJta:5Lmw8y1] 713018 dOybc @ 867696 pqosoj L rGB fhmmAj41N:75MlS:HI02Dhtvv92:9 2187/03/19=1987/05/20 * (C) x4MFm08t8:Rv484 ; pteIOL8G3L7K:PiF:ed01NaE:5 jLXaJ7:TPkx:hnc0a84T949V -337155Pt [z Cimq6yQ5 6Hz:4:y j3pTBDWr:Xwi5xa4fT0] egJUlL927254 @ ETLYp 830216 ; i5s0qZi5:it:qv1g 35vrb4GY:4y5 (aGO8o973:8:DqP588) cReiuY -813613 @ ZVwoPf 932860 [iG0OQ:tWl:Y:F59 R09pKiI] 499724 y @ qwy 1.437329 ; bNO myx5K3DhtxI0gZGc4eWCtZ:Nsb:l7Yx 3v [OT2sQn:78mn:6dC6k2BPLg8Xu:1NV8GHg6cq9d] yFpTo -290498 @ PdADxe 2.729485 W2C:PzB:a7iA:VHeXq7gYKI vM2611:bCoq aHgK 381854 @ 721958RKv pPYXx2wrFvMC:94zr3C9T1TdhR6D4 2187/03/25 * (R) Z6et66o5F44:HG74D5gdk9 ; I8k7lSGq5223eFvFeSX4B20O EiOxe -49170.3ptFX @ WYtmyT 781596 [cn U0zGj:Ylm2R:4a Sy] pxLB-155187 x:yw8duO9Ic:4CqN:AUa xd 2187/03/27 * (tg) VirzaV9213J8:jkhY7b (Kk:22T6893q129HLfMQ9QZf K66Kc:1 Qu:51Ki) hEP288640 @ 1.734694 ES [yP7cI1x:Ns7Ik:CF 8W:2r:tT8r:ND3K4naIL] vnCq-270931 @ 368422 Qum [OkJ:WvF64D87M22b58xgnAS2yf85] -542640 eM [P U] 602501 oUB c32Ww93:Z:mj7BK 2187/03/28=1987/05/21 ! (kIWM) lz L5 (HoA7y0U4v4R458U80K0wqj4V8L:3Hy hK:u2o) baf 692841 (tX) -864521 x @ Vy0.360209 ; Wxe043p9:061 (Lq:5g7L:wwv:HyHFPCZYy) MYpd631128 @ 508656Cz (x5 79ER 2Z3I0U19 47VfLtNKOq:2X) -696823bSC ; a8:Ad6LdI 2187/03/29=1987/05/24 * (O) E0Rfh34 t4FdLx3:l:Ck6m3VJ stAj-908528 ; vQ8h:OiGS:f7CG 5 Y7fBz d10 z 2936srygkDWhn1c4jyG:L:nHpTrnLB51 vG75178.3 ; UfL7dG5a87Mm430vKtQ4Z:M2sdMzh3:a [bdA:M:AM9FCXQm6HMGIB7 Lz:YV4tm3:r:SN] 38780.8HvaL @ HamR 19.0182771 ; z4KY C:Q:3h2 X 7 (R:n9:lXU) YTx-417744 @ 1.230378 D [DLs 9 mtV 1G:b:Bep yVU n93Hbd:8By2Mx:M] -48707.2 a [R:Goz1E0Bj:blF5Hl] -298794rPVm @ 3.283965 bUoyr ; KYa8M9:6482B650mXUE7b:Ka w8NK7U54q7vHGU0l30t:Il:3P:U T:SfY0 2187/04/01=1987/05/26 ! (Eq) M59Ml:3 K ASPeZQ:7LW:wzN2QaUr77OXr wS-848544 @ Exd 0.625834 (tX0 1Uhq2Q969Z5x07I4B:8:n2t4T4V:E:6K) rQem -637441 ; ErSB:G0z RqXG2 IZT646tE30:n84Y fEt-767950 @ 0.594623 Hkqq ; pw3k2j 4KT D3Uo:B27e:WKDiuOD43L [C3] BheR-157970 (A8p:062sBPYe8e:6oK:n 8X:87Qn) 982770 uVsEo ; oHE U:F7z1f1b8mTv:s 2KwNo7 WgI [D20EU:1] Sppd 837068 Bdn0znI799:07:s60 M5099z7 ; dEGfxH:U8p6z8DBSZ:diiQ49H21 6 2187/04/04 * (M80a) Z:k206 [x7:anc] 437316 Dqk @ eZCzj 0.285032 [hC705 NtL:iP:q3608P93d7P:GdE] 544670li ; oy:4zAY3upE31j0VUKCo95b84:O9E5d365E:4 ysN:P:56bd43Z:v:krkEyf R y287D ; eZ U0AU4:MSGD:jjK7O94rD3webUWW1:4r0:07 2187/04/07 (vvA) wc05O32W:H34:Y990 jBCX1J524A63U1k1j98823 2PYK886n 391730qA tf50 ptOxzto:d6O0ye:cg8MIIR9C3ZlTRQ1 nx920305 @ 1.019439 MyVL (UaTIt08Xl47:98zv83X92k3Ap) nlfkS 154433 (j3M44pR2dc4J:rTP7oN 2kO5 GOp:2zo8I) PXO-126823 ; yM21414i0eV2HAu50r61:5WN:2ApRhE:sM:ic Xs551d 14OR5:j:5:u ; psjmtc 57 BvF44CTmU7BXYna9w:2e58Wk5dV 2187/04/12 * (Rh2) xe:4694A4vNXtTO:BRdX4n:U:6gLXW4atziLPi h:E31A:6lSbJKE181aNzg2l2s1q -158224QX @ dKAD 4.682267 (pfitL560:H:rL2O7L) 976398 wMZ @ t486509 Motxoa 3u1 D 58373.9 V @ 3.1142171 a ; tnBH6o 757IL 3:bche8 5 V:6 F29 [U 76 7PO6E2M6fJ47pk3no K1KI6r] 690776 cL ; Tz1W zMl:6r0Sr67wwiv6mHI1:UMowDl (A:4s97lX2ZJg5XL:JASmf:H) wTEdPr 126890 ; E8N231t1v62wd53wZ3S7ZJ [MkN6f0006:53MX36298W vL2Rr] 131653voz i:Y82PTu8o0RsU5c393 ; m09zvwT873z02 2187/04/16 * (O) g09nmf1f ; fiQ T4D2:E pNwv4w va614303 @ BfEt776882 [z 8M3qc:0W6 323:7 HE46vE5goXpC4C] GZatQD349279 ; c79 N6Ny7D0NikU2Qzy:W:N4M:94F7D:C zua3Gr ol-473902 @ TiG1.58947 ; USUm8 I01T7:rL9xC4H6QaI1fI4zg4iw9D9D94NY iK9PGH2Lr:5xL 11077X ; WiNki QxGAnH:0i1:5:hpK1454yx 2187/04/18=1987/05/27 (d6 76) WKycS8BB4k2pWB6U1 TqC799Q3Q:Q:uvDbP v ; S1 H:53Fz:Z9RY7660oS2:iEzlx8zId1KHlAp13 WAIZL7YC8T6xsxb C lEBkQZcC6ZYX8oUdjs aBsqCO-859192 ; X (dVeDho1zLvBM1:bc2907v rw6w:fIyj) rJO-348003 @ 0.5828lNOAP [VYR86aOPCBtU:T43s31qNrcmX55] 148376Kaqj ; Hb5Mz2T33FTGp:6mf92L1 g0P FOnuBD634440 Eu2590k:6 542890 YbTkPd ; MG7:fX1QN7F:aBWcZ WK:miVS9IS4 TXhdCnW:q3 83Q0:A:TLa:P1 389207Qu @ ZxD 474844 ; ze Xr:DiUmY4O5a494 E 7C jd9f:y ej:OV1p ; UQIvt cFi75q3l7:33q9V4epmq1c3o:MQY9:4 2187/04/19=1987/05/29 * (yIEGx) vtu yZ:FLNg8 bz81244o 1 [eQ0:vQFftKq312 5:qt4OJRF14:8iUS9] -514867H [zS4kj:1U364n 77yT4heLR0WCtP:J:qpTVC] C 626719 ; k61HYqVgh4pw4PSJ:8Zd5c0LvKhEqop hQu J5:iL7i:dlczgv5 aUXZ:SW20:d ; B6CQoX:m7:T0DObHKf:c35E tVQg:6N7 XR0yk1F 2187/04/25 (d5) mO3V65tcQ93f:bpH2O4KQc7i8A3ubo64L [cY8j:X:D1g53O] 913440 AkznZ ; qiWp0:1K:9:yoagiids8YU8GazTJnDZyJMuJjU [h 6v3:GS9 8] w 909579.0 @ 0.439321 a QpWryY4 2187/04/26 * (Oa X8v) nTu ; Blnf RcSi7T4:bA87Zk v1a6Bue2RiF3Q:1vHkrC Yd-594954 @ 379530 uXzDxT (jlB2E47ka6U7mwZ3Tp:56wE:w Gr5pco7WO) MJ -210135 (HrjMwB92A2:4:bnLz1hf5EAIx8wo71 AnSM83) PzifkG-18466.2 @ 532525.0 a a E913Z48r PEZ930954 @ TWN0.26211 K4:3w6:okgb7jHi3He7DMv:h6N:W15Go9 ; OLa p:p6ITR 2187/04/28 ! (eq) I87a 1T 0f95 (wD) ZGCAmF-128472 ; h6161Ck29nuU0N764bi 4g:0W (Ri:q26Y9xE ubzMbbB672:pM0:QA) 68525.6QBwZl @ 750604 joWS 2187/04/30=1987/06/03 ! (B) tv2:A:T22kI16n5J6mbHh:qZj:E:86 (ktL4z m2B5cKS4m53CgD:MnTTq584P8:6) 655291 eOEUll @ 594372OkUXd ; upZep79v (bYT3j0yJy8fIq:2O48R70YV5T34) -498403 RaGA @ 1.611509AfRXi G:2XSwTH3:WI7Y8:H:Kk 134595OIIOE ; p:38 jzL2NZhQjy5fIB0S:ixVU0 [BTLZ Y7Kd8W:mo6rR] -142806 ZUSM @ iV629749 ; VRFPZ40e:B8vMk8VYpyaxYT T1f1jY8qHg69LxTr (QzQvt5:a) 863612 C ; UW7ldN1R6:JV79EzO9I5nkQKqXNx (K0jG1Nn6xW6qmGoK852Wm82P891w O) w -639451.0 ; b2ERs49v6Ri734X01 STI0MT ; kpM7x:a6:n61Z1k7 uY4ABsJ93 2187/05/06=1987/06/07 ! (DWS3) G Z r2r iYLKfxR7:vrl9vr707P6pb 94XSd HC5FR0S vxXRGK -142673 @ xAV 605535 [Fy1:u15syG 8:8C 38vB:178ExtQk5eBMq7F:blU] -130770oz QvJZ:t xw:N 2187/05/09 * (nKVz6) a9:T7 ; eT75RyW4Xd8a f1a679UnNi SH065V KnOo9 l7liL:4Qnz07kFUQ 800903 emTA @ 976479MTcm (a:d) -761443Ay @ 0.576885 TDa i 2187/05/12=1987/06/09 ! (G3CN) nm r5 (rsiBbx8T EXY5) 386928 flTii @ 2.000225 DE HQg3:FK1EgO -941250jElq ; Yn6gjO C42Y z:mOpuX:f:nnk:tx 2187/05/13 * (r6e y0) V65by ; Z X8Whns [h96nQFF32fyO8y] 968994 UBNDg ; T72YEmnsvcmFX91DLNMg w2948464 [Qwk40WBS0:N2V WUW:BM7M4:09961G0] 130897QkeP @ 719156 E l5C402g:8ym9I0T1id zl7:sa811F1q vbUv:8Q ; t5AnGg88R45W:S161kRC0o >>>2 === 0 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-sort-all.test��������������������������������������������������������0000664�0000000�0000000�00000012035�14411236400�0020754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/11 LIAT Expenses:Travel:Airfare $40.00 Liabilities:MasterCard 2008/01/14 cheaptickets.com Expenses:Travel:Airfare $182.19 Liabilities:MasterCard 2008/02/05 CTX Expenses:Travel:Auto $240.38 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/08/08 BCIS I-131 FILING FEE- Expenses:Travel:Passport $170.00 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/22 AGNT FEE Expenses:Travel:Airfare $70.00 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 LIAT 1974 LIMITED Expenses:Travel:Airfare $418.34 Liabilities:MasterCard 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking test reg --monthly --sort=-amount 08-Jan-01 - 08-Jan-31 Expense:Travel:Airfare $222.19 $222.19 Liabilities:MasterCard $-222.19 0 08-Feb-01 - 08-Feb-29 Expense:Travel:Airfare $477.60 $477.60 Expenses:Travel:Auto $280.97 $758.57 Liabilities:MasterCard $-758.57 0 08-Mar-01 - 08-Mar-31 Expense:Travel:Airfare $2,463.20 $2,463.20 Liabilities:MasterCard $-2,463.20 0 08-Apr-01 - 08-Apr-30 Expense:Travel:Airfare $1,186.14 $1,186.14 Liabilities:MasterCard $-1,186.14 0 08-Aug-01 - 08-Aug-31 Expens:Travel:Passport $170.00 $170.00 Liabilities:MasterCard $-170.00 0 08-Sep-01 - 08-Sep-30 Expense:Travel:Airfare $3,925.94 $3,925.94 Liabilities:MasterCard $-3,925.94 0 08-Dec-01 - 08-Dec-31 Expens:Travel:Passport $254.00 $254.00 Assets:Checking $-254.00 0 end test test reg --monthly --sort-all=-amount 08-Sep-01 - 08-Sep-30 Expense:Travel:Airfare $3,925.94 $3,925.94 08-Mar-01 - 08-Mar-31 Expense:Travel:Airfare $2,463.20 $6,389.14 08-Apr-01 - 08-Apr-30 Expense:Travel:Airfare $1,186.14 $7,575.28 08-Feb-01 - 08-Feb-29 Expense:Travel:Airfare $477.60 $8,052.88 Expenses:Travel:Auto $280.97 $8,333.85 08-Dec-01 - 08-Dec-31 Expens:Travel:Passport $254.00 $8,587.85 08-Jan-01 - 08-Jan-31 Expense:Travel:Airfare $222.19 $8,810.04 08-Aug-01 - 08-Aug-31 Expens:Travel:Passport $170.00 $8,980.04 Liabilities:MasterCard $-170.00 $8,810.04 08-Jan-01 - 08-Jan-31 Liabilities:MasterCard $-222.19 $8,587.85 08-Dec-01 - 08-Dec-31 Assets:Checking $-254.00 $8,333.85 08-Feb-01 - 08-Feb-29 Liabilities:MasterCard $-758.57 $7,575.28 08-Apr-01 - 08-Apr-30 Liabilities:MasterCard $-1,186.14 $6,389.14 08-Mar-01 - 08-Mar-31 Liabilities:MasterCard $-2,463.20 $3,925.94 08-Sep-01 - 08-Sep-30 Liabilities:MasterCard $-3,925.94 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-sort-xacts.test������������������������������������������������������0000664�0000000�0000000�00000022441�14411236400�0021330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/11 LIAT Expenses:Travel:Airfare $40.00 Liabilities:MasterCard 2008/01/14 cheaptickets.com Expenses:Travel:Airfare $182.19 Liabilities:MasterCard 2008/02/05 CTX Expenses:Travel:Auto $240.38 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/08/08 BCIS I-131 FILING FEE- Expenses:Travel:Passport $170.00 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/22 AGNT FEE Expenses:Travel:Airfare $70.00 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 LIAT 1974 LIMITED Expenses:Travel:Airfare $418.34 Liabilities:MasterCard 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking test reg --sort=account 08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-127.00 08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-254.00 08-Jan-11 LIAT Expense:Travel:Airfare $40.00 $-214.00 08-Jan-14 cheaptickets.com Expense:Travel:Airfare $182.19 $-31.81 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $206.99 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $445.79 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $1,677.39 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $2,908.99 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,064.85 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,220.71 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $3,657.92 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $4,095.13 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $5,007.73 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $5,920.33 08-Sep-22 AGNT FEE Expense:Travel:Airfare $70.00 $5,990.33 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $6,796.53 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $7,602.73 08-Sep-22 LIAT 1974 LIMITED Expense:Travel:Airfare $418.34 $8,021.07 08-Feb-05 CTX Expenses:Travel:Auto $240.38 $8,261.45 08-Feb-22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 $8,302.04 08-Aug-08 BCIS I-131 FILING F.. Expens:Travel:Passport $170.00 $8,472.04 08-Dec-26 U.S. Department of .. Expens:Travel:Passport $127.00 $8,599.04 08-Dec-26 U.S. Department of .. Expens:Travel:Passport $127.00 $8,726.04 08-Jan-11 LIAT Liabilities:MasterCard $-40.00 $8,686.04 08-Jan-14 cheaptickets.com Liabilities:MasterCard $-182.19 $8,503.85 08-Feb-05 CTX Liabilities:MasterCard $-240.38 $8,263.47 08-Feb-05 UNITED Liabilities:MasterCard $-238.80 $8,024.67 08-Feb-05 UNITED Liabilities:MasterCard $-238.80 $7,785.87 08-Feb-22 BUDGET RENT-A-CAR Liabilities:MasterCard $-40.59 $7,745.28 08-Mar-16 IBERIA Liabilities:MasterCard $-1,231.60 $6,513.68 08-Mar-16 IBERIA Liabilities:MasterCard $-1,231.60 $5,282.08 08-Apr-03 AMERICAN Liabilities:MasterCard $-155.86 $5,126.22 08-Apr-03 AMERICAN Liabilities:MasterCard $-155.86 $4,970.36 08-Apr-30 UNITED Liabilities:MasterCard $-437.21 $4,533.15 08-Apr-30 UNITED Liabilities:MasterCard $-437.21 $4,095.94 08-Aug-08 BCIS I-131 FILING F.. Liabilities:MasterCard $-170.00 $3,925.94 08-Sep-06 AMERICAN Liabilities:MasterCard $-912.60 $3,013.34 08-Sep-06 AMERICAN Liabilities:MasterCard $-912.60 $2,100.74 08-Sep-22 AGNT FEE Liabilities:MasterCard $-70.00 $2,030.74 08-Sep-22 DELTA Liabilities:MasterCard $-806.20 $1,224.54 08-Sep-22 DELTA Liabilities:MasterCard $-806.20 $418.34 08-Sep-22 LIAT 1974 LIMITED Liabilities:MasterCard $-418.34 0 end test test reg --sort-xacts=account 08-Jan-11 LIAT Expense:Travel:Airfare $40.00 $40.00 Liabilities:MasterCard $-40.00 0 08-Jan-14 cheaptickets.com Expense:Travel:Airfare $182.19 $182.19 Liabilities:MasterCard $-182.19 0 08-Feb-05 CTX Expenses:Travel:Auto $240.38 $240.38 Liabilities:MasterCard $-240.38 0 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $238.80 Liabilities:MasterCard $-238.80 0 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $238.80 Liabilities:MasterCard $-238.80 0 08-Feb-22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 $40.59 Liabilities:MasterCard $-40.59 0 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $1,231.60 Liabilities:MasterCard $-1,231.60 0 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $1,231.60 Liabilities:MasterCard $-1,231.60 0 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $155.86 Liabilities:MasterCard $-155.86 0 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $155.86 Liabilities:MasterCard $-155.86 0 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $437.21 Liabilities:MasterCard $-437.21 0 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $437.21 Liabilities:MasterCard $-437.21 0 08-Aug-08 BCIS I-131 FILING F.. Expens:Travel:Passport $170.00 $170.00 Liabilities:MasterCard $-170.00 0 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $912.60 Liabilities:MasterCard $-912.60 0 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $912.60 Liabilities:MasterCard $-912.60 0 08-Sep-22 AGNT FEE Expense:Travel:Airfare $70.00 $70.00 Liabilities:MasterCard $-70.00 0 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $806.20 Liabilities:MasterCard $-806.20 0 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $806.20 Liabilities:MasterCard $-806.20 0 08-Sep-22 LIAT 1974 LIMITED Expense:Travel:Airfare $418.34 $418.34 Liabilities:MasterCard $-418.34 0 08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-127.00 Expens:Travel:Passport $127.00 0 08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-127.00 Expens:Travel:Passport $127.00 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-sort.test������������������������������������������������������������0000664�0000000�0000000�00000030012�14411236400�0020201�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/11 LIAT Expenses:Travel:Airfare $40.00 Liabilities:MasterCard 2008/01/14 cheaptickets.com Expenses:Travel:Airfare $182.19 Liabilities:MasterCard 2008/02/05 CTX Expenses:Travel:Auto $240.38 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/08/08 BCIS I-131 FILING FEE- Expenses:Travel:Passport $170.00 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/22 AGNT FEE Expenses:Travel:Airfare $70.00 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 LIAT 1974 LIMITED Expenses:Travel:Airfare $418.34 Liabilities:MasterCard 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking test reg airfare --sort=date 08-Jan-11 LIAT Expense:Travel:Airfare $40.00 $40.00 08-Jan-14 cheaptickets.com Expense:Travel:Airfare $182.19 $222.19 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $460.99 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $699.79 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $1,931.39 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $3,162.99 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,318.85 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,474.71 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $3,911.92 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $4,349.13 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $5,261.73 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $6,174.33 08-Sep-22 AGNT FEE Expense:Travel:Airfare $70.00 $6,244.33 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $7,050.53 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $7,856.73 08-Sep-22 LIAT 1974 LIMITED Expense:Travel:Airfare $418.34 $8,275.07 end test test reg airfare --sort=date,amount 08-Jan-11 LIAT Expense:Travel:Airfare $40.00 $40.00 08-Jan-14 cheaptickets.com Expense:Travel:Airfare $182.19 $222.19 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $460.99 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $699.79 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $1,931.39 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $3,162.99 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,318.85 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,474.71 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $3,911.92 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $4,349.13 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $5,261.73 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $6,174.33 08-Sep-22 AGNT FEE Expense:Travel:Airfare $70.00 $6,244.33 08-Sep-22 LIAT 1974 LIMITED Expense:Travel:Airfare $418.34 $6,662.67 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $7,468.87 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $8,275.07 end test test reg airfare --sort=date,-amount 08-Jan-11 LIAT Expense:Travel:Airfare $40.00 $40.00 08-Jan-14 cheaptickets.com Expense:Travel:Airfare $182.19 $222.19 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $460.99 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $699.79 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $1,931.39 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $3,162.99 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,318.85 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,474.71 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $3,911.92 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $4,349.13 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $5,261.73 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $6,174.33 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $6,980.53 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $7,786.73 08-Sep-22 LIAT 1974 LIMITED Expense:Travel:Airfare $418.34 $8,205.07 08-Sep-22 AGNT FEE Expense:Travel:Airfare $70.00 $8,275.07 end test test reg airfare --sort=-date,-amount 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $806.20 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $1,612.40 08-Sep-22 LIAT 1974 LIMITED Expense:Travel:Airfare $418.34 $2,030.74 08-Sep-22 AGNT FEE Expense:Travel:Airfare $70.00 $2,100.74 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $3,013.34 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $3,925.94 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $4,363.15 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $4,800.36 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $4,956.22 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $5,112.08 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $6,343.68 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $7,575.28 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $7,814.08 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $8,052.88 08-Jan-14 cheaptickets.com Expense:Travel:Airfare $182.19 $8,235.07 08-Jan-11 LIAT Expense:Travel:Airfare $40.00 $8,275.07 end test test bal --sort=total $-8,726.04 Liabilities:MasterCard $-254.00 Assets:Checking $8,980.04 Expenses:Travel $280.97 Auto $424.00 Passport $8,275.07 Airfare -------------------- 0 end test test bal --sort=-total $8,980.04 Expenses:Travel $8,275.07 Airfare $424.00 Passport $280.97 Auto $-254.00 Assets:Checking $-8,726.04 Liabilities:MasterCard -------------------- 0 end test test bal --sort=-account $-8,726.04 Liabilities:MasterCard $8,980.04 Expenses:Travel $424.00 Passport $280.97 Auto $8,275.07 Airfare $-254.00 Assets:Checking -------------------- 0 end test test reg --sort=account 08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-127.00 08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-254.00 08-Jan-11 LIAT Expense:Travel:Airfare $40.00 $-214.00 08-Jan-14 cheaptickets.com Expense:Travel:Airfare $182.19 $-31.81 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $206.99 08-Feb-05 UNITED Expense:Travel:Airfare $238.80 $445.79 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $1,677.39 08-Mar-16 IBERIA Expense:Travel:Airfare $1,231.60 $2,908.99 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,064.85 08-Apr-03 AMERICAN Expense:Travel:Airfare $155.86 $3,220.71 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $3,657.92 08-Apr-30 UNITED Expense:Travel:Airfare $437.21 $4,095.13 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $5,007.73 08-Sep-06 AMERICAN Expense:Travel:Airfare $912.60 $5,920.33 08-Sep-22 AGNT FEE Expense:Travel:Airfare $70.00 $5,990.33 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $6,796.53 08-Sep-22 DELTA Expense:Travel:Airfare $806.20 $7,602.73 08-Sep-22 LIAT 1974 LIMITED Expense:Travel:Airfare $418.34 $8,021.07 08-Feb-05 CTX Expenses:Travel:Auto $240.38 $8,261.45 08-Feb-22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 $8,302.04 08-Aug-08 BCIS I-131 FILING F.. Expens:Travel:Passport $170.00 $8,472.04 08-Dec-26 U.S. Department of .. Expens:Travel:Passport $127.00 $8,599.04 08-Dec-26 U.S. Department of .. Expens:Travel:Passport $127.00 $8,726.04 08-Jan-11 LIAT Liabilities:MasterCard $-40.00 $8,686.04 08-Jan-14 cheaptickets.com Liabilities:MasterCard $-182.19 $8,503.85 08-Feb-05 CTX Liabilities:MasterCard $-240.38 $8,263.47 08-Feb-05 UNITED Liabilities:MasterCard $-238.80 $8,024.67 08-Feb-05 UNITED Liabilities:MasterCard $-238.80 $7,785.87 08-Feb-22 BUDGET RENT-A-CAR Liabilities:MasterCard $-40.59 $7,745.28 08-Mar-16 IBERIA Liabilities:MasterCard $-1,231.60 $6,513.68 08-Mar-16 IBERIA Liabilities:MasterCard $-1,231.60 $5,282.08 08-Apr-03 AMERICAN Liabilities:MasterCard $-155.86 $5,126.22 08-Apr-03 AMERICAN Liabilities:MasterCard $-155.86 $4,970.36 08-Apr-30 UNITED Liabilities:MasterCard $-437.21 $4,533.15 08-Apr-30 UNITED Liabilities:MasterCard $-437.21 $4,095.94 08-Aug-08 BCIS I-131 FILING F.. Liabilities:MasterCard $-170.00 $3,925.94 08-Sep-06 AMERICAN Liabilities:MasterCard $-912.60 $3,013.34 08-Sep-06 AMERICAN Liabilities:MasterCard $-912.60 $2,100.74 08-Sep-22 AGNT FEE Liabilities:MasterCard $-70.00 $2,030.74 08-Sep-22 DELTA Liabilities:MasterCard $-806.20 $1,224.54 08-Sep-22 DELTA Liabilities:MasterCard $-806.20 $418.34 08-Sep-22 LIAT 1974 LIMITED Liabilities:MasterCard $-418.34 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-start-of-week.test���������������������������������������������������0000664�0000000�0000000�00000013341�14411236400�0021710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --weekly --start-of-week=mon books 07-Dec-31 - 08-Jan-06 Expenses:Books $10.00 $10.00 08-Jan-28 - 08-Feb-03 Expenses:Books $30.00 $40.00 08-Feb-25 - 08-Mar-02 Expenses:Books $50.00 $90.00 08-Mar-31 - 08-Apr-06 Expenses:Books $70.00 $160.00 08-Apr-28 - 08-May-04 Expenses:Books $90.00 $250.00 08-May-26 - 08-Jun-01 Expenses:Books $110.00 $360.00 08-Jun-30 - 08-Jul-06 Expenses:Books $130.00 $490.00 08-Jul-28 - 08-Aug-03 Expenses:Books $150.00 $640.00 08-Aug-25 - 08-Aug-31 Expenses:Books $80.00 $720.00 08-Sep-01 - 08-Sep-07 Expenses:Books $90.00 $810.00 08-Sep-29 - 08-Oct-05 Expenses:Books $190.00 $1000.00 08-Oct-27 - 08-Nov-02 Expenses:Books $210.00 $1210.00 08-Nov-24 - 08-Nov-30 Expenses:Books $110.00 $1320.00 08-Dec-01 - 08-Dec-07 Expenses:Books $120.00 $1440.00 08-Dec-29 - 09-Jan-04 Expenses:Books $130.00 $1570.00 09-Jan-26 - 09-Feb-01 Expenses:Books $30.00 $1600.00 09-Feb-23 - 09-Mar-01 Expenses:Books $50.00 $1650.00 09-Mar-30 - 09-Apr-05 Expenses:Books $70.00 $1720.00 09-Apr-27 - 09-May-03 Expenses:Books $90.00 $1810.00 09-May-25 - 09-May-31 Expenses:Books $50.00 $1860.00 09-Jun-01 - 09-Jun-07 Expenses:Books $60.00 $1920.00 09-Jun-29 - 09-Jul-05 Expenses:Books $130.00 $2050.00 09-Jul-27 - 09-Aug-02 Expenses:Books $150.00 $2200.00 09-Aug-31 - 09-Sep-06 Expenses:Books $170.00 $2370.00 09-Sep-28 - 09-Oct-04 Expenses:Books $190.00 $2560.00 09-Oct-26 - 09-Nov-01 Expenses:Books $210.00 $2770.00 09-Nov-30 - 09-Dec-06 Expenses:Books $230.00 $3000.00 09-Dec-28 - 10-Jan-03 Expenses:Books $120.00 $3120.00 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-strict.test����������������������������������������������������������0000664�0000000�0000000�00000002352�14411236400�0020530�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 * Foo Expenses:Food $10.00 Assets:Cash 2007/02/03 Bar * Expenses:Food $20.00 Assets:Cash 2007/02/02 Baz Expenses:Foodx $30.00 Assets:Cash test reg --strict 07-Feb-02 Foo Expenses:Food $10.00 $10.00 Assets:Cash $-10.00 0 07-Feb-03 Bar Expenses:Food $20.00 $20.00 Assets:Cash $-20.00 0 07-Feb-02 Baz Expenses:Foodx $30.00 $30.00 Assets:Cash $-30.00 0 __ERROR__ Warning: "$FILE", line 2: Unknown account 'Expenses:Food' Warning: "$FILE", line 2: Unknown commodity '$' Warning: "$FILE", line 3: Unknown account 'Assets:Cash' Warning: "$FILE", line 6: Unknown account 'Expenses:Food' Warning: "$FILE", line 6: Unknown commodity '$' Warning: "$FILE", line 7: Unknown account 'Assets:Cash' Warning: "$FILE", line 10: Unknown account 'Expenses:Foodx' Warning: "$FILE", line 10: Unknown commodity '$' Warning: "$FILE", line 11: Unknown account 'Assets:Cash' end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-subtotal.test��������������������������������������������������������0000664�0000000�0000000�00000005766�14411236400�0021071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/11 LIAT Expenses:Travel:Airfare $40.00 Liabilities:MasterCard 2008/01/14 cheaptickets.com Expenses:Travel:Airfare $182.19 Liabilities:MasterCard 2008/02/05 CTX Expenses:Travel:Auto $240.38 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/08/08 BCIS I-131 FILING FEE- Expenses:Travel:Passport $170.00 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/22 AGNT FEE Expenses:Travel:Airfare $70.00 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 LIAT 1974 LIMITED Expenses:Travel:Airfare $418.34 Liabilities:MasterCard 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking test reg --subtotal 08-Jan-11 - 08-Dec-26 Assets:Checking $-254.00 $-254.00 Expense:Travel:Airfare $8,275.07 $8,021.07 Expenses:Travel:Auto $280.97 $8,302.04 Expens:Travel:Passport $424.00 $8,726.04 Liabilities:MasterCard $-8,726.04 0 end test ����������ledger-3.3.2/test/baseline/opt-tail.test������������������������������������������������������������0000664�0000000�0000000�00000012152�14411236400�0020150�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --tail=10 books 09-Aug-01 August Expenses:Books $80.00 $2200.00 09-Aug-31 End of August Expenses:Books $80.00 $2280.00 09-Sep-01 September Expenses:Books $90.00 $2370.00 09-Sep-30 End of September Expenses:Books $90.00 $2460.00 09-Oct-01 October Expenses:Books $100.00 $2560.00 09-Oct-31 End of October Expenses:Books $100.00 $2660.00 09-Nov-01 November Expenses:Books $110.00 $2770.00 09-Nov-30 End of November Expenses:Books $110.00 $2880.00 09-Dec-01 December Expenses:Books $120.00 $3000.00 09-Dec-31 End of December Expenses:Books $120.00 $3120.00 end test test reg --last=10 books 09-Aug-01 August Expenses:Books $80.00 $2200.00 09-Aug-31 End of August Expenses:Books $80.00 $2280.00 09-Sep-01 September Expenses:Books $90.00 $2370.00 09-Sep-30 End of September Expenses:Books $90.00 $2460.00 09-Oct-01 October Expenses:Books $100.00 $2560.00 09-Oct-31 End of October Expenses:Books $100.00 $2660.00 09-Nov-01 November Expenses:Books $110.00 $2770.00 09-Nov-30 End of November Expenses:Books $110.00 $2880.00 09-Dec-01 December Expenses:Books $120.00 $3000.00 09-Dec-31 End of December Expenses:Books $120.00 $3120.00 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-time-colon.test������������������������������������������������������0000664�0000000�0000000�00000002553�14411236400�0021271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2013/04/05 () Meeting Tactical (Internal:Meeting:Tactical) 1800s @ ($36/3600) 2013/04/05 () Email (CustomerA:Email) 300s 2013/04/05 () Config (CustomerB:Config) 5100s 2013/04/05 () Walk (Personal:Walk) 1800s 2013/04/05 () Lunch (Personal:Lunch) 5400s test bal 5.0m CustomerA:Email 1.42h CustomerB:Config 30.0m Internal:Meeting:Tactical 2.00h Personal 1.50h Lunch 30.0m Walk -------------------- 4.00h end test test bal --time-colon 5:0m CustomerA:Email 1:25h CustomerB:Config 30:0m Internal:Meeting:Tactical 2:00h Personal 1:30h Lunch 30:0m Walk -------------------- 4:00h end test test reg --time-colon 13-Apr-05 Meeting Tactical (Int:Meeting:Tactical) 30:0m 30:0m 13-Apr-05 Email (CustomerA:Email) 5:0m 35:0m 13-Apr-05 Config (CustomerB:Config) 1:25h 2:00h 13-Apr-05 Walk (Personal:Walk) 30:0m 2:30h 13-Apr-05 Lunch (Personal:Lunch) 1:30h 4:00h end test �����������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-time-report.test�����������������������������������������������������0000664�0000000�0000000�00000003334�14411236400�0021470�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������i 2013/04/05 09:30:00 Internal:Meeting:Tactical [Intelligent comment] o 2013/04/05 10:00:00 i 2013/04/05 10:00:00 CustomerA:Email o 2013/04/05 10:05:00 i 2013/04/05 10:05:00 CustomerB:Config o 2013/04/05 11:30:00 i 2013/04/05 11:30:00 Personal:Walk o 2013/04/05 12:00:00 i 2013/04/05 12:00:00 Personal:Lunch o 2013/04/05 13:30:00 test bal 5.0m CustomerA:Email 1.42h CustomerB:Config 30.0m Internal:Meeting:Tactical [Intelligent comment] 2.00h Personal 1.50h Lunch 30.0m Walk -------------------- 4.00h end test test bal --time-report 13-Apr-05 10:00:00 13-Apr-05 10:05:00 5.0m CustomerA:Email 13-Apr-05 10:05:00 13-Apr-05 11:30:00 1.42h CustomerB:Config 13-Apr-05 09:30:00 13-Apr-05 10:00:00 30.0m Internal:Meeting:Tactical [Intelligent comment] 2.00h Personal 13-Apr-05 12:00:00 13-Apr-05 13:30:00 1.50h Lunch 13-Apr-05 11:30:00 13-Apr-05 12:00:00 30.0m Walk -------------------------------------------------- end test test bal --time-report --time-colon 13-Apr-05 10:00:00 13-Apr-05 10:05:00 5:0m CustomerA:Email 13-Apr-05 10:05:00 13-Apr-05 11:30:00 1:25h CustomerB:Config 13-Apr-05 09:30:00 13-Apr-05 10:00:00 30:0m Internal:Meeting:Tactical [Intelligent comment] 2:00h Personal 13-Apr-05 12:00:00 13-Apr-05 13:30:00 1:30h Lunch 13-Apr-05 11:30:00 13-Apr-05 12:00:00 30:0m Walk -------------------------------------------------- end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-total-data.test������������������������������������������������������0000664�0000000�0000000�00000000307�14411236400�0021250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --market --total-data 2007-02-02 0.35 2007-02-02 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-total-width.test�����������������������������������������������������0000664�0000000�0000000�00000000705�14411236400�0021460�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --total-width=25 07-Feb-02 RD VMMXX As:Investm:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Dividen:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test �����������������������������������������������������������ledger-3.3.2/test/baseline/opt-total.test�����������������������������������������������������������0000664�0000000�0000000�00000000501�14411236400�0020335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --total=10 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 10 In:Divid:Vanguar:VMMXX $-0.35 10 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-trace.test�����������������������������������������������������������0000664�0000000�0000000�00000001471�14411236400�0020317�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 ; Using values with two or more digits as the argument to the --trace option ; resulted in a segmentation fault. ; Since ledger prints debugging information to stderr when the --trace option ; was given and that debugging information contains timing information, e.g. [1ms] ; which is likely to differ on each test run, this test only checks that ledger ; does not crash when the --trace options was specified. test reg --trace 10 2>/dev/null 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 0.350 VMMXX end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-truncate.test��������������������������������������������������������0000664�0000000�0000000�00000014775�14411236400�0021061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/11 LIAT Expenses:Travel:Airfare $40.00 Liabilities:MasterCard 2008/01/14 cheaptickets.com Expenses:Travel:Airfare $182.19 Liabilities:MasterCard 2008/02/05 CTX Expenses:Travel:Auto $240.38 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/08/08 BCIS I-131 FILING FEE- Expenses:Travel:Passport $170.00 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/22 AGNT FEE Expenses:Travel:Airfare $70.00 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 LIAT 1974 LIMITED Expenses:Travel:Airfare $418.34 Liabilities:MasterCard 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking test reg airfare --truncate=leading 08-Jan-11 LIAT ..enses:Travel:Airfare $40.00 $40.00 08-Jan-14 cheaptickets.com ..enses:Travel:Airfare $182.19 $222.19 08-Feb-05 UNITED ..enses:Travel:Airfare $238.80 $460.99 08-Feb-05 UNITED ..enses:Travel:Airfare $238.80 $699.79 08-Mar-16 IBERIA ..enses:Travel:Airfare $1,231.60 $1,931.39 08-Mar-16 IBERIA ..enses:Travel:Airfare $1,231.60 $3,162.99 08-Apr-03 AMERICAN ..enses:Travel:Airfare $155.86 $3,318.85 08-Apr-03 AMERICAN ..enses:Travel:Airfare $155.86 $3,474.71 08-Apr-30 UNITED ..enses:Travel:Airfare $437.21 $3,911.92 08-Apr-30 UNITED ..enses:Travel:Airfare $437.21 $4,349.13 08-Sep-06 AMERICAN ..enses:Travel:Airfare $912.60 $5,261.73 08-Sep-06 AMERICAN ..enses:Travel:Airfare $912.60 $6,174.33 08-Sep-22 AGNT FEE ..enses:Travel:Airfare $70.00 $6,244.33 08-Sep-22 DELTA ..enses:Travel:Airfare $806.20 $7,050.53 08-Sep-22 DELTA ..enses:Travel:Airfare $806.20 $7,856.73 08-Sep-22 LIAT 1974 LIMITED ..enses:Travel:Airfare $418.34 $8,275.07 end test test reg airfare --truncate=middle 08-Jan-11 LIAT Expenses:T..el:Airfare $40.00 $40.00 08-Jan-14 cheaptickets.com Expenses:T..el:Airfare $182.19 $222.19 08-Feb-05 UNITED Expenses:T..el:Airfare $238.80 $460.99 08-Feb-05 UNITED Expenses:T..el:Airfare $238.80 $699.79 08-Mar-16 IBERIA Expenses:T..el:Airfare $1,231.60 $1,931.39 08-Mar-16 IBERIA Expenses:T..el:Airfare $1,231.60 $3,162.99 08-Apr-03 AMERICAN Expenses:T..el:Airfare $155.86 $3,318.85 08-Apr-03 AMERICAN Expenses:T..el:Airfare $155.86 $3,474.71 08-Apr-30 UNITED Expenses:T..el:Airfare $437.21 $3,911.92 08-Apr-30 UNITED Expenses:T..el:Airfare $437.21 $4,349.13 08-Sep-06 AMERICAN Expenses:T..el:Airfare $912.60 $5,261.73 08-Sep-06 AMERICAN Expenses:T..el:Airfare $912.60 $6,174.33 08-Sep-22 AGNT FEE Expenses:T..el:Airfare $70.00 $6,244.33 08-Sep-22 DELTA Expenses:T..el:Airfare $806.20 $7,050.53 08-Sep-22 DELTA Expenses:T..el:Airfare $806.20 $7,856.73 08-Sep-22 LIAT 1974 LIMITED Expenses:T..el:Airfare $418.34 $8,275.07 end test test reg airfare --truncate=trailing 08-Jan-11 LIAT Expenses:Travel:Airf.. $40.00 $40.00 08-Jan-14 cheaptickets.com Expenses:Travel:Airf.. $182.19 $222.19 08-Feb-05 UNITED Expenses:Travel:Airf.. $238.80 $460.99 08-Feb-05 UNITED Expenses:Travel:Airf.. $238.80 $699.79 08-Mar-16 IBERIA Expenses:Travel:Airf.. $1,231.60 $1,931.39 08-Mar-16 IBERIA Expenses:Travel:Airf.. $1,231.60 $3,162.99 08-Apr-03 AMERICAN Expenses:Travel:Airf.. $155.86 $3,318.85 08-Apr-03 AMERICAN Expenses:Travel:Airf.. $155.86 $3,474.71 08-Apr-30 UNITED Expenses:Travel:Airf.. $437.21 $3,911.92 08-Apr-30 UNITED Expenses:Travel:Airf.. $437.21 $4,349.13 08-Sep-06 AMERICAN Expenses:Travel:Airf.. $912.60 $5,261.73 08-Sep-06 AMERICAN Expenses:Travel:Airf.. $912.60 $6,174.33 08-Sep-22 AGNT FEE Expenses:Travel:Airf.. $70.00 $6,244.33 08-Sep-22 DELTA Expenses:Travel:Airf.. $806.20 $7,050.53 08-Sep-22 DELTA Expenses:Travel:Airf.. $806.20 $7,856.73 08-Sep-22 LIAT 1974 LIMITED Expenses:Travel:Airf.. $418.34 $8,275.07 end test ���ledger-3.3.2/test/baseline/opt-unbudgeted.test������������������������������������������������������0000664�0000000�0000000�00000021755�14411236400�0021356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ monthly Expenses:Books $10.00 Assets:Cash 2008/01/01 January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Liabilities:Cards $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Liabilities:Cards $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Liabilities:Cards $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Liabilities:Cards $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Liabilities:Cards $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Liabilities:Cards $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Liabilities:Cards $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Liabilities:Cards $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Liabilities:Cards $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Liabilities:Cards $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Liabilities:Cards $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Liabilities:Cards $120.00 Assets:Cash test reg --unbudgeted 08-Jan-01 January Liabilities:Cards $10.00 $10.00 08-Jan-31 End of January Liabilities:Cards $10.00 $20.00 08-Feb-01 February Liabilities:Cards $20.00 $40.00 08-Feb-28 End of February Liabilities:Cards $20.00 $60.00 08-Mar-01 March Liabilities:Cards $30.00 $90.00 08-Mar-31 End of March Liabilities:Cards $30.00 $120.00 08-Apr-01 April Liabilities:Cards $40.00 $160.00 08-Apr-30 End of April Liabilities:Cards $40.00 $200.00 08-May-01 May Liabilities:Cards $50.00 $250.00 08-May-31 End of May Liabilities:Cards $50.00 $300.00 08-Jun-01 June Liabilities:Cards $60.00 $360.00 08-Jun-30 End of June Liabilities:Cards $60.00 $420.00 08-Jul-01 July Liabilities:Cards $70.00 $490.00 08-Jul-31 End of July Liabilities:Cards $70.00 $560.00 08-Aug-01 August Liabilities:Cards $80.00 $640.00 08-Aug-31 End of August Liabilities:Cards $80.00 $720.00 08-Sep-01 September Liabilities:Cards $90.00 $810.00 08-Sep-30 End of September Liabilities:Cards $90.00 $900.00 08-Oct-01 October Liabilities:Cards $100.00 $1000.00 08-Oct-31 End of October Liabilities:Cards $100.00 $1100.00 08-Nov-01 November Liabilities:Cards $110.00 $1210.00 08-Nov-30 End of November Liabilities:Cards $110.00 $1320.00 08-Dec-01 December Liabilities:Cards $120.00 $1440.00 08-Dec-31 End of December Liabilities:Cards $120.00 $1560.00 09-Jan-01 January Liabilities:Cards $10.00 $1570.00 09-Jan-31 End of January Liabilities:Cards $10.00 $1580.00 09-Feb-01 February Liabilities:Cards $20.00 $1600.00 09-Feb-28 End of February Liabilities:Cards $20.00 $1620.00 09-Mar-01 March Liabilities:Cards $30.00 $1650.00 09-Mar-31 End of March Liabilities:Cards $30.00 $1680.00 09-Apr-01 April Liabilities:Cards $40.00 $1720.00 09-Apr-30 End of April Liabilities:Cards $40.00 $1760.00 09-May-01 May Liabilities:Cards $50.00 $1810.00 09-May-31 End of May Liabilities:Cards $50.00 $1860.00 09-Jun-01 June Liabilities:Cards $60.00 $1920.00 09-Jun-30 End of June Liabilities:Cards $60.00 $1980.00 09-Jul-01 July Liabilities:Cards $70.00 $2050.00 09-Jul-31 End of July Liabilities:Cards $70.00 $2120.00 09-Aug-01 August Liabilities:Cards $80.00 $2200.00 09-Aug-31 End of August Liabilities:Cards $80.00 $2280.00 09-Sep-01 September Liabilities:Cards $90.00 $2370.00 09-Sep-30 End of September Liabilities:Cards $90.00 $2460.00 09-Oct-01 October Liabilities:Cards $100.00 $2560.00 09-Oct-31 End of October Liabilities:Cards $100.00 $2660.00 09-Nov-01 November Liabilities:Cards $110.00 $2770.00 09-Nov-30 End of November Liabilities:Cards $110.00 $2880.00 09-Dec-01 December Liabilities:Cards $120.00 $3000.00 09-Dec-31 End of December Liabilities:Cards $120.00 $3120.00 end test �������������������ledger-3.3.2/test/baseline/opt-uncleared.test�������������������������������������������������������0000664�0000000�0000000�00000025162�14411236400�0021166�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 ! February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 * March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April * Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April * Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 ! Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 ! Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --uncleared 08-Jan-31 End of January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 08-Feb-01 February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 08-Feb-28 End of February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 08-Mar-31 End of March Expenses:Books $30.00 $30.00 Assets:Cash $-30.00 0 08-Apr-01 April Assets:Cash $-40.00 $-40.00 08-Apr-30 End of April Assets:Cash $-40.00 $-80.00 08-May-01 May Expenses:Books $50.00 $-30.00 Assets:Cash $-50.00 $-80.00 08-May-31 End of May Expenses:Books $50.00 $-30.00 Assets:Cash $-50.00 $-80.00 08-Jun-01 June Expenses:Books $60.00 $-20.00 Assets:Cash $-60.00 $-80.00 08-Jun-30 End of June Expenses:Books $60.00 $-20.00 Assets:Cash $-60.00 $-80.00 08-Jul-01 July Expenses:Books $70.00 $-10.00 Assets:Cash $-70.00 $-80.00 08-Jul-31 End of July Expenses:Books $70.00 $-10.00 Assets:Cash $-70.00 $-80.00 08-Aug-01 August Expenses:Books $80.00 0 Assets:Cash $-80.00 $-80.00 08-Aug-31 End of August Expenses:Books $80.00 0 Assets:Cash $-80.00 $-80.00 08-Sep-01 September Expenses:Books $90.00 $10.00 Assets:Cash $-90.00 $-80.00 08-Sep-30 End of September Expenses:Books $90.00 $10.00 Assets:Cash $-90.00 $-80.00 08-Oct-01 October Expenses:Books $100.00 $20.00 Assets:Cash $-100.00 $-80.00 08-Oct-31 End of October Expenses:Books $100.00 $20.00 Assets:Cash $-100.00 $-80.00 08-Nov-01 November Expenses:Books $110.00 $30.00 Assets:Cash $-110.00 $-80.00 08-Nov-30 End of November Expenses:Books $110.00 $30.00 Assets:Cash $-110.00 $-80.00 08-Dec-01 December Expenses:Books $120.00 $40.00 Assets:Cash $-120.00 $-80.00 08-Dec-31 End of December Expenses:Books $120.00 $40.00 Assets:Cash $-120.00 $-80.00 09-Jan-01 January Expenses:Books $10.00 $-70.00 Assets:Cash $-10.00 $-80.00 09-Jan-31 End of January Expenses:Books $10.00 $-70.00 Assets:Cash $-10.00 $-80.00 09-Feb-01 February Expenses:Books $20.00 $-60.00 Assets:Cash $-20.00 $-80.00 09-Feb-28 End of February Expenses:Books $20.00 $-60.00 Assets:Cash $-20.00 $-80.00 09-Mar-01 March Expenses:Books $30.00 $-50.00 Assets:Cash $-30.00 $-80.00 09-Mar-31 End of March Expenses:Books $30.00 $-50.00 Assets:Cash $-30.00 $-80.00 09-Apr-01 April Expenses:Books $40.00 $-40.00 Assets:Cash $-40.00 $-80.00 09-Apr-30 End of April Expenses:Books $40.00 $-40.00 Assets:Cash $-40.00 $-80.00 09-May-01 May Expenses:Books $50.00 $-30.00 Assets:Cash $-50.00 $-80.00 09-May-31 End of May Expenses:Books $50.00 $-30.00 Assets:Cash $-50.00 $-80.00 09-Jun-01 June Expenses:Books $60.00 $-20.00 Assets:Cash $-60.00 $-80.00 09-Jun-30 End of June Expenses:Books $60.00 $-20.00 Assets:Cash $-60.00 $-80.00 09-Jul-01 July Expenses:Books $70.00 $-10.00 Assets:Cash $-70.00 $-80.00 09-Jul-31 End of July Expenses:Books $70.00 $-10.00 Assets:Cash $-70.00 $-80.00 09-Aug-01 August Expenses:Books $80.00 0 Assets:Cash $-80.00 $-80.00 09-Aug-31 End of August Expenses:Books $80.00 0 Assets:Cash $-80.00 $-80.00 09-Sep-01 September Expenses:Books $90.00 $10.00 Assets:Cash $-90.00 $-80.00 09-Sep-30 End of September Expenses:Books $90.00 $10.00 Assets:Cash $-90.00 $-80.00 09-Oct-01 October Expenses:Books $100.00 $20.00 Assets:Cash $-100.00 $-80.00 09-Oct-31 End of October Expenses:Books $100.00 $20.00 Assets:Cash $-100.00 $-80.00 09-Nov-01 November Expenses:Books $110.00 $30.00 Assets:Cash $-110.00 $-80.00 09-Nov-30 End of November Expenses:Books $110.00 $30.00 Assets:Cash $-110.00 $-80.00 09-Dec-01 December Expenses:Books $120.00 $40.00 Assets:Cash $-120.00 $-80.00 09-Dec-31 End of December Expenses:Books $120.00 $40.00 Assets:Cash $-120.00 $-80.00 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-unrealized-gains.test������������������������������������������������0000664�0000000�0000000�00000000701�14411236400�0022455�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/10/01 Sample Assets:Brokerage 10 AAPL Assets:Checking $-200.00 P 2008/10/20 12:00:00 AAPL $30.00 ; 2008/10/20 <Generated Transaction> ; Assets:Brokerage $100 ; Equity:Unrealized Gains test bal -V --unrealized --unrealized-gains G $100.00 Assets $300.00 Brokerage $-200.00 Checking $-100.00 G -------------------- 0 end test ���������������������������������������������������������������ledger-3.3.2/test/baseline/opt-unrealized-losses.test�����������������������������������������������0000664�0000000�0000000�00000000702�14411236400�0022665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/10/01 Sample Assets:Brokerage 10 AAPL Assets:Checking $-200.00 P 2008/10/20 12:00:00 AAPL $10.00 ; 2008/10/20 <Generated Transaction> ; Assets:Brokerage $100 ; Equity:Unrealized Gains test bal -V --unrealized --unrealized-losses L $-100.00 Assets $100.00 Brokerage $-200.00 Checking $100.00 L -------------------- 0 end test ��������������������������������������������������������������ledger-3.3.2/test/baseline/opt-unrealized.test������������������������������������������������������0000664�0000000�0000000�00000001167�14411236400�0021365�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/10/01 Sample Assets:Brokerage 10 AAPL Assets:Checking $-200.00 2008/10/01 Sample Assets:Brokerage -10 QQQQ Assets:Checking $1000 P 2008/10/20 12:00:00 AAPL $30.00 P 2008/10/20 12:00:00 QQQQ $110 ; 2008/10/20 <Generated Transaction> ; Assets:Brokerage $100 ; Equity:Unrealized Gains ; 2008/10/20 <Generated Transaction> ; Assets:Brokerage $-100 ; Equity:Unrealized Losses test bal -V --unrealized 0 Assets $-800.00 Brokerage $800.00 Checking -------------------- 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-unround.test���������������������������������������������������������0000664�0000000�0000000�00000005501�14411236400�0020711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/11 LIAT Expenses:Travel:Airfare $40.00 Liabilities:MasterCard 2008/01/14 cheaptickets.com Expenses:Travel:Airfare $182.19 Liabilities:MasterCard 2008/02/05 CTX Expenses:Travel:Auto $240.38 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare $238.80 Liabilities:MasterCard 2008/02/22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare $1,231.60 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare $155.86 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare $437.21 Liabilities:MasterCard 2008/08/08 BCIS I-131 FILING FEE- Expenses:Travel:Passport $170.00 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare $912.60 Liabilities:MasterCard 2008/09/22 AGNT FEE Expenses:Travel:Airfare $70.00 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare $806.20 Liabilities:MasterCard 2008/09/22 LIAT 1974 LIMITED Expenses:Travel:Airfare $418.34 Liabilities:MasterCard 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking 2008/12/26 U.S. Department of State Expenses:Travel:Passport $127.00 Assets:Checking test bal --percent --unround 100.00% Assets:Checking 100.00% Expenses:Travel 92.14958953% Airfare 3.12882793% Auto 4.72158253% Passport 100.00% Liabilities:MasterCard end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-value-expr.test������������������������������������������������������0000664�0000000�0000000�00000002451�14411236400�0021310�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ D 1000.00 EUR D 1000.00 USD D 1000.00 DM 2015-01-01 * Buy 2 FOO Assets:Investments 2 FOO @@ 20.00 EUR Assets:Cash -20.00 EUR 2015-05-01 * Spend on food Expenses:Food 20.00 USD ; Just to be silly, always valuate *these* $20 as 30 DM, no matter what ; the user asks for with -V or -X ; VALUE:: 30 DM Assets:Cash -20.00 USD P 2015-05-01 USD 20 DM P 2015-06-01 USD 22 DM test bal assets:investments -V --value-expr "25.00 EUR" 50.00 EUR Assets:Investments end test test bal assets:investments -G --value-expr "date < [March 2015] ? 22.00 EUR : 25.00 EUR" --now "2015-02-20" 24.00 EUR Assets:Investments end test test bal assets:investments -G --value-expr "date < [March 2015] ? 22.00 EUR : 25.00 EUR" --now "2015-03-20" 30.00 EUR Assets:Investments end test test bal expenses:food 20.00 USD Expenses:Food end test test bal expenses:food -V 600.00 DM Expenses:Food end test test bal expenses:food -X "DM" --now "2015-05-02" 600.00 DM Expenses:Food end test test bal expenses:food -X "DM" --now "2015-06-02" 600.00 DM Expenses:Food end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-values.test����������������������������������������������������������0000664�0000000�0000000�00000000155�14411236400�0020516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test tags -f test/input/drewr3.dat --values hastag: not block hastag: true nestedtag: true nobudget end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-weekly.test����������������������������������������������������������0000664�0000000�0000000�00000013436�14411236400�0020525�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --weekly books 07-Dec-30 - 08-Jan-05 Expenses:Books $10.00 $10.00 08-Jan-27 - 08-Feb-02 Expenses:Books $30.00 $40.00 08-Feb-24 - 08-Mar-01 Expenses:Books $50.00 $90.00 08-Mar-30 - 08-Apr-05 Expenses:Books $70.00 $160.00 08-Apr-27 - 08-May-03 Expenses:Books $90.00 $250.00 08-May-25 - 08-May-31 Expenses:Books $50.00 $300.00 08-Jun-01 - 08-Jun-07 Expenses:Books $60.00 $360.00 08-Jun-29 - 08-Jul-05 Expenses:Books $130.00 $490.00 08-Jul-27 - 08-Aug-02 Expenses:Books $150.00 $640.00 08-Aug-31 - 08-Sep-06 Expenses:Books $170.00 $810.00 08-Sep-28 - 08-Oct-04 Expenses:Books $190.00 $1000.00 08-Oct-26 - 08-Nov-01 Expenses:Books $210.00 $1210.00 08-Nov-30 - 08-Dec-06 Expenses:Books $230.00 $1440.00 08-Dec-28 - 09-Jan-03 Expenses:Books $130.00 $1570.00 09-Jan-25 - 09-Jan-31 Expenses:Books $10.00 $1580.00 09-Feb-01 - 09-Feb-07 Expenses:Books $20.00 $1600.00 09-Feb-22 - 09-Feb-28 Expenses:Books $20.00 $1620.00 09-Mar-01 - 09-Mar-07 Expenses:Books $30.00 $1650.00 09-Mar-29 - 09-Apr-04 Expenses:Books $70.00 $1720.00 09-Apr-26 - 09-May-02 Expenses:Books $90.00 $1810.00 09-May-31 - 09-Jun-06 Expenses:Books $110.00 $1920.00 09-Jun-28 - 09-Jul-04 Expenses:Books $130.00 $2050.00 09-Jul-26 - 09-Aug-01 Expenses:Books $150.00 $2200.00 09-Aug-30 - 09-Sep-05 Expenses:Books $170.00 $2370.00 09-Sep-27 - 09-Oct-03 Expenses:Books $190.00 $2560.00 09-Oct-25 - 09-Oct-31 Expenses:Books $100.00 $2660.00 09-Nov-01 - 09-Nov-07 Expenses:Books $110.00 $2770.00 09-Nov-29 - 09-Dec-05 Expenses:Books $230.00 $3000.00 09-Dec-27 - 10-Jan-02 Expenses:Books $120.00 $3120.00 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-weekly_empty.test����������������������������������������������������0000664�0000000�0000000�00000027462�14411236400�0021747�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --weekly --empty books 07-Dec-30 - 08-Jan-05 Expenses:Books $10.00 $10.00 08-Jan-06 - 08-Jan-12 <None> 0 $10.00 08-Jan-13 - 08-Jan-19 <None> 0 $10.00 08-Jan-20 - 08-Jan-26 <None> 0 $10.00 08-Jan-27 - 08-Feb-02 Expenses:Books $30.00 $40.00 08-Feb-03 - 08-Feb-09 <None> 0 $40.00 08-Feb-10 - 08-Feb-16 <None> 0 $40.00 08-Feb-17 - 08-Feb-23 <None> 0 $40.00 08-Feb-24 - 08-Mar-01 Expenses:Books $50.00 $90.00 08-Mar-02 - 08-Mar-08 <None> 0 $90.00 08-Mar-09 - 08-Mar-15 <None> 0 $90.00 08-Mar-16 - 08-Mar-22 <None> 0 $90.00 08-Mar-23 - 08-Mar-29 <None> 0 $90.00 08-Mar-30 - 08-Apr-05 Expenses:Books $70.00 $160.00 08-Apr-06 - 08-Apr-12 <None> 0 $160.00 08-Apr-13 - 08-Apr-19 <None> 0 $160.00 08-Apr-20 - 08-Apr-26 <None> 0 $160.00 08-Apr-27 - 08-May-03 Expenses:Books $90.00 $250.00 08-May-04 - 08-May-10 <None> 0 $250.00 08-May-11 - 08-May-17 <None> 0 $250.00 08-May-18 - 08-May-24 <None> 0 $250.00 08-May-25 - 08-May-31 Expenses:Books $50.00 $300.00 08-Jun-01 - 08-Jun-07 Expenses:Books $60.00 $360.00 08-Jun-08 - 08-Jun-14 <None> 0 $360.00 08-Jun-15 - 08-Jun-21 <None> 0 $360.00 08-Jun-22 - 08-Jun-28 <None> 0 $360.00 08-Jun-29 - 08-Jul-05 Expenses:Books $130.00 $490.00 08-Jul-06 - 08-Jul-12 <None> 0 $490.00 08-Jul-13 - 08-Jul-19 <None> 0 $490.00 08-Jul-20 - 08-Jul-26 <None> 0 $490.00 08-Jul-27 - 08-Aug-02 Expenses:Books $150.00 $640.00 08-Aug-03 - 08-Aug-09 <None> 0 $640.00 08-Aug-10 - 08-Aug-16 <None> 0 $640.00 08-Aug-17 - 08-Aug-23 <None> 0 $640.00 08-Aug-24 - 08-Aug-30 <None> 0 $640.00 08-Aug-31 - 08-Sep-06 Expenses:Books $170.00 $810.00 08-Sep-07 - 08-Sep-13 <None> 0 $810.00 08-Sep-14 - 08-Sep-20 <None> 0 $810.00 08-Sep-21 - 08-Sep-27 <None> 0 $810.00 08-Sep-28 - 08-Oct-04 Expenses:Books $190.00 $1000.00 08-Oct-05 - 08-Oct-11 <None> 0 $1000.00 08-Oct-12 - 08-Oct-18 <None> 0 $1000.00 08-Oct-19 - 08-Oct-25 <None> 0 $1000.00 08-Oct-26 - 08-Nov-01 Expenses:Books $210.00 $1210.00 08-Nov-02 - 08-Nov-08 <None> 0 $1210.00 08-Nov-09 - 08-Nov-15 <None> 0 $1210.00 08-Nov-16 - 08-Nov-22 <None> 0 $1210.00 08-Nov-23 - 08-Nov-29 <None> 0 $1210.00 08-Nov-30 - 08-Dec-06 Expenses:Books $230.00 $1440.00 08-Dec-07 - 08-Dec-13 <None> 0 $1440.00 08-Dec-14 - 08-Dec-20 <None> 0 $1440.00 08-Dec-21 - 08-Dec-27 <None> 0 $1440.00 08-Dec-28 - 09-Jan-03 Expenses:Books $130.00 $1570.00 09-Jan-04 - 09-Jan-10 <None> 0 $1570.00 09-Jan-11 - 09-Jan-17 <None> 0 $1570.00 09-Jan-18 - 09-Jan-24 <None> 0 $1570.00 09-Jan-25 - 09-Jan-31 Expenses:Books $10.00 $1580.00 09-Feb-01 - 09-Feb-07 Expenses:Books $20.00 $1600.00 09-Feb-08 - 09-Feb-14 <None> 0 $1600.00 09-Feb-15 - 09-Feb-21 <None> 0 $1600.00 09-Feb-22 - 09-Feb-28 Expenses:Books $20.00 $1620.00 09-Mar-01 - 09-Mar-07 Expenses:Books $30.00 $1650.00 09-Mar-08 - 09-Mar-14 <None> 0 $1650.00 09-Mar-15 - 09-Mar-21 <None> 0 $1650.00 09-Mar-22 - 09-Mar-28 <None> 0 $1650.00 09-Mar-29 - 09-Apr-04 Expenses:Books $70.00 $1720.00 09-Apr-05 - 09-Apr-11 <None> 0 $1720.00 09-Apr-12 - 09-Apr-18 <None> 0 $1720.00 09-Apr-19 - 09-Apr-25 <None> 0 $1720.00 09-Apr-26 - 09-May-02 Expenses:Books $90.00 $1810.00 09-May-03 - 09-May-09 <None> 0 $1810.00 09-May-10 - 09-May-16 <None> 0 $1810.00 09-May-17 - 09-May-23 <None> 0 $1810.00 09-May-24 - 09-May-30 <None> 0 $1810.00 09-May-31 - 09-Jun-06 Expenses:Books $110.00 $1920.00 09-Jun-07 - 09-Jun-13 <None> 0 $1920.00 09-Jun-14 - 09-Jun-20 <None> 0 $1920.00 09-Jun-21 - 09-Jun-27 <None> 0 $1920.00 09-Jun-28 - 09-Jul-04 Expenses:Books $130.00 $2050.00 09-Jul-05 - 09-Jul-11 <None> 0 $2050.00 09-Jul-12 - 09-Jul-18 <None> 0 $2050.00 09-Jul-19 - 09-Jul-25 <None> 0 $2050.00 09-Jul-26 - 09-Aug-01 Expenses:Books $150.00 $2200.00 09-Aug-02 - 09-Aug-08 <None> 0 $2200.00 09-Aug-09 - 09-Aug-15 <None> 0 $2200.00 09-Aug-16 - 09-Aug-22 <None> 0 $2200.00 09-Aug-23 - 09-Aug-29 <None> 0 $2200.00 09-Aug-30 - 09-Sep-05 Expenses:Books $170.00 $2370.00 09-Sep-06 - 09-Sep-12 <None> 0 $2370.00 09-Sep-13 - 09-Sep-19 <None> 0 $2370.00 09-Sep-20 - 09-Sep-26 <None> 0 $2370.00 09-Sep-27 - 09-Oct-03 Expenses:Books $190.00 $2560.00 09-Oct-04 - 09-Oct-10 <None> 0 $2560.00 09-Oct-11 - 09-Oct-17 <None> 0 $2560.00 09-Oct-18 - 09-Oct-24 <None> 0 $2560.00 09-Oct-25 - 09-Oct-31 Expenses:Books $100.00 $2660.00 09-Nov-01 - 09-Nov-07 Expenses:Books $110.00 $2770.00 09-Nov-08 - 09-Nov-14 <None> 0 $2770.00 09-Nov-15 - 09-Nov-21 <None> 0 $2770.00 09-Nov-22 - 09-Nov-28 <None> 0 $2770.00 09-Nov-29 - 09-Dec-05 Expenses:Books $230.00 $3000.00 09-Dec-06 - 09-Dec-12 <None> 0 $3000.00 09-Dec-13 - 09-Dec-19 <None> 0 $3000.00 09-Dec-20 - 09-Dec-26 <None> 0 $3000.00 09-Dec-27 - 10-Jan-02 Expenses:Books $120.00 $3120.00 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-wide.test������������������������������������������������������������0000664�0000000�0000000�00000001030�14411236400�0020140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test reg --wide 07-Feb-02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX 0.350 VMMXX Income:Dividends:Vanguard:VMMXX $-0.35 $-0.35 0.350 VMMXX end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-xact-no-payee.test���������������������������������������������������0000664�0000000�0000000�00000002055�14411236400�0021672�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������define DDV=1.22 define DDV_ONLY=0.22 define MACH=€1.12 P 2018/01/01 00:00:00 MACH_FULL_P €1 P 2018/01/01 00:00:00 MACH_FULL €1 P 2020/04/30 00:00:00 MACH_FULL €1 P 2020/05/02 00:00:00 MACH_FULL €1 P 2020/11/16 00:00:00 MACH_FULL €2 P 2021/06/01 00:00:00 MACH_BASIC €5 ; Automated MACH handling. Match all purchases of MACH ; TODO: 2 pairs, cleared, uncleared = expr account =~ /Assets:Inventory:MACH$/ & payee =~ /DADA D.O.O./ & comment =~ /.*Automate$/ Expenses:Products:MACH (€1.0 * amount * P(1 MACH_FULL_P, date)) DDV (€1.0 * amount * P(1 MACH_FULL_P, date) * DDV_ONLY) Liabilities:Payable:Dada (-€1.0 * amount * P(1 MACH_FULL_P, date) * DDV) ~every month from 2021/01/01 Sales:Hardware:MACH 60 MACH_FULL Assets:Inventory:MACH -60 MACH_FULL Sales:Hardware:MACH -10.20€ DDV -32.44€ Assets:Receivable:Duda test bal end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/baseline/opt-yearly.test����������������������������������������������������������0000664�0000000�0000000�00000007223�14411236400�0020527�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 January Expenses:Books $10.00 Assets:Cash 2008/01/31 End of January Expenses:Books $10.00 Assets:Cash 2008/02/01 February Expenses:Books $20.00 Assets:Cash 2008/02/28 End of February Expenses:Books $20.00 Assets:Cash 2008/03/01 March Expenses:Books $30.00 Assets:Cash 2008/03/31 End of March Expenses:Books $30.00 Assets:Cash 2008/04/01 April Expenses:Books $40.00 Assets:Cash 2008/04/30 End of April Expenses:Books $40.00 Assets:Cash 2008/05/01 May Expenses:Books $50.00 Assets:Cash 2008/05/31 End of May Expenses:Books $50.00 Assets:Cash 2008/06/01 June Expenses:Books $60.00 Assets:Cash 2008/06/30 End of June Expenses:Books $60.00 Assets:Cash 2008/07/01 July Expenses:Books $70.00 Assets:Cash 2008/07/31 End of July Expenses:Books $70.00 Assets:Cash 2008/08/01 August Expenses:Books $80.00 Assets:Cash 2008/08/31 End of August Expenses:Books $80.00 Assets:Cash 2008/09/01 September Expenses:Books $90.00 Assets:Cash 2008/09/30 End of September Expenses:Books $90.00 Assets:Cash 2008/10/01 October Expenses:Books $100.00 Assets:Cash 2008/10/31 End of October Expenses:Books $100.00 Assets:Cash 2008/11/01 November Expenses:Books $110.00 Assets:Cash 2008/11/30 End of November Expenses:Books $110.00 Assets:Cash 2008/12/01 December Expenses:Books $120.00 Assets:Cash 2008/12/31 End of December Expenses:Books $120.00 Assets:Cash 2009/01/01 January Expenses:Books $10.00 Assets:Cash 2009/01/31 End of January Expenses:Books $10.00 Assets:Cash 2009/02/01 February Expenses:Books $20.00 Assets:Cash 2009/02/28 End of February Expenses:Books $20.00 Assets:Cash 2009/03/01 March Expenses:Books $30.00 Assets:Cash 2009/03/31 End of March Expenses:Books $30.00 Assets:Cash 2009/04/01 April Expenses:Books $40.00 Assets:Cash 2009/04/30 End of April Expenses:Books $40.00 Assets:Cash 2009/05/01 May Expenses:Books $50.00 Assets:Cash 2009/05/31 End of May Expenses:Books $50.00 Assets:Cash 2009/06/01 June Expenses:Books $60.00 Assets:Cash 2009/06/30 End of June Expenses:Books $60.00 Assets:Cash 2009/07/01 July Expenses:Books $70.00 Assets:Cash 2009/07/31 End of July Expenses:Books $70.00 Assets:Cash 2009/08/01 August Expenses:Books $80.00 Assets:Cash 2009/08/31 End of August Expenses:Books $80.00 Assets:Cash 2009/09/01 September Expenses:Books $90.00 Assets:Cash 2009/09/30 End of September Expenses:Books $90.00 Assets:Cash 2009/10/01 October Expenses:Books $100.00 Assets:Cash 2009/10/31 End of October Expenses:Books $100.00 Assets:Cash 2009/11/01 November Expenses:Books $110.00 Assets:Cash 2009/11/30 End of November Expenses:Books $110.00 Assets:Cash 2009/12/01 December Expenses:Books $120.00 Assets:Cash 2009/12/31 End of December Expenses:Books $120.00 Assets:Cash test reg --yearly books 08-Jan-01 - 08-Dec-31 Expenses:Books $1560.00 $1560.00 09-Jan-01 - 09-Dec-31 Expenses:Books $1560.00 $3120.00 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/convert.py������������������������������������������������������������������������0000775�0000000�0000000�00000017362�14411236400�0016001�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # convert.py: This script converts a Boost.Test unit test into an # equivalent Python unit test. # # Copyright (c) 2003-2023, John Wiegley. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # - Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # - Neither the name of New Artisans LLC nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import re import sys import os source = os.path.realpath(sys.argv[1]) base = os.path.splitext(source)[0] target = os.path.realpath(sys.argv[2]) dirname = os.path.dirname(target) if not os.path.isdir(dirname): try: os.makedirs(dirname) except: pass fd = open(source, "r") fo = open(target, "w") fo.write('''# -*- coding: utf-8 -*- import unittest import exceptions import operator from ledger import * from StringIO import * from datetime import * internalAmount = Amount.exact class %sTestCase(unittest.TestCase): testSession = None''' % os.path.basename(base)) not_for_python = 0 class_name = None for line in fd.readlines(): if re.match('^#ifndef NOT_FOR_PYTHON', line): not_for_python += 1 continue elif not_for_python > 0: if re.match('^#endif // NOT_FOR_PYTHON', line): not_for_python -= 1 continue if re.match('^(using|CPP|[#{}/])', line): continue if re.match('^\s+[{}]\s+$', line): continue if not re.search('assert', line): match = re.match('^};', line) if match: continue match = re.match('BOOST_.*_TEST_SUITE', line) if match: continue match = re.match('^struct (.*?) {', line) if match: class_name = match.group(1) continue if class_name: match = re.search('(~)?%s\(\) {' % class_name, line) if match: if match.group(1): fo.write(' def tearDown(self):\n') else: fo.write(' def setUp(self):\n') continue match = re.match('BOOST_AUTO_TEST_CASE\((.+?)\)', line) if match: fo.write(' def %s(self):\n' % match.group(1)) continue match = re.match('void [^:]+::(test[^(]+|setUp|tearDown)\(\)', line) if match: fo.write(' def %s(self):\n' % match.group(1)) continue match = re.search(' ([a-z:_<>]+?)&?\s+([a-z0-9_]+)(\((.+?)\))?;', line) if match: if match.group(1) != "std::string": line = ' %s = %s(%s)\n' % (match.group(2), match.group(1), match.group(4) or "") else: line = '' match = re.search(' ([a-z:_<>]+?)&?\s+([a-z0-9]+)\s*=\s*([^(]+);', line) if match: line = ' %s = %s(%s)\n' % (match.group(2), match.group(1), match.group(3)) match = re.search(' ([a-z:_<>]+?)\s+([a-z0-9]+)\s*=\s*(.+?)$', line) if match: line = ' %s = %s\n' % (match.group(2), match.group(3)) line = re.sub('BOOST_CHECK_NE', 'self.assertNotEqual', line) line = re.sub('BOOST_CHECK_EQUAL', 'self.assertEqual', line) line = re.sub('BOOST_CHECK_THROW\(([^,]+), ([^,)]+?)\)', 'self.assertRaises(\\2, lambda: \\1)', line) line = re.sub('BOOST_CHECK', 'self.assertTrue', line) # jww (2010-06-20): Determine this list automatically by scanning # the class_ lines in src/py_*.cc line = re.sub('amount_t::precision_t\(([^)]+?)\)', '\\1', line) line = re.sub('amount_t::', 'Amount.', line) line = re.sub('Amount\.PARSE_', 'AmountParse.', line) line = re.sub('commodity_t\(([^)]+?)\)', '\\1', line) line = re.sub('commodity_t::', 'Commodity.', line) line = re.sub('balance_t::', 'Balance.', line) line = re.sub('balance_pair_t::', 'BalancePair.', line) line = re.sub('value_t::', 'Value.', line) line = re.sub('amount_t', 'Amount', line) line = re.sub('commodity_t', 'Commodity', line) line = re.sub('balance_t', 'Balance', line) line = re.sub('balance_pair_t', 'BalancePair', line) line = re.sub('value_t', 'Value', line) line = re.sub("PARSE_DEFAULT", "ParseFlags.Default", line) line = re.sub("PARSE_PARTIAL", "ParseFlags.Partial", line) line = re.sub("PARSE_SINGLE", "ParseFlags.Single", line) line = re.sub("PARSE_NO_MIGRATE", "ParseFlags.NoMigrate", line) line = re.sub("PARSE_NO_REDUCE", "ParseFlags.NoReduce", line) line = re.sub("PARSE_NO_ASSIGN", "ParseFlags.NoAssign", line) line = re.sub("PARSE_NO_DATES", "ParseFlags.NoDates", line) line = re.sub("PARSE_OP_CONTEXT", "ParseFlags.OpContext", line) line = re.sub("PARSE_SOFT_FAIL", "ParseFlags.SoftFail", line) line = re.sub('ledger::', '', line) line = re.sub('std::istringstream', 'StringIO', line) line = re.sub('std::ostringstream', 'StringIO', line) line = re.sub('set_session_context\(&session\)', 'self.testSession = session()\n set_session_context(self.testSession)', line) line = re.sub('set_session_context\(\)', 'set_session_context()\n self.testSession = None', line) line = re.sub('([a-z_]+?)_t\b', '\\1', line) line = re.sub('("[^"]+")', 'u\\1', line) line = re.sub('std::string\(([^)]+?)\)', '\\1', line) line = re.sub('string\(([^)]+?)\)', '\\1', line) line = re.sub('\.print\(([^)]+?)\)', '.print_(\\1)', line) line = re.sub('true', 'True', line) line = re.sub('false', 'False', line) line = re.sub('CURRENT_TIME\(\)', 'datetime.now()', line) line = re.sub('CURRENT_DATE\(\)', 'date.today()', line) line = re.sub('commodity\(\)', 'commodity', line) line = re.sub('precision\(\)', 'precision', line) line = re.sub('([0-9]+)[FL]', '\\1', line) line = re.sub('([0-9]+)UL', '\\1L', line) line = re.sub(';', '', line) line = re.sub('//', '#', line) line = re.sub('->', '.', line) line = re.sub('(\s+|\()(\S+?) \? (.+?) : (.+?)\)', '\\1\\3 if \\2 else \\4)', line) line = re.sub('if \((.+?)\)( {)?$', 'if \\1:', line) line = re.sub('(} )?else( {)?$', 'else:', line) line = re.sub('amount_error', 'exceptions.ArithmeticError', line) match = re.match('^ ', line) if match: fo.write(' ' + line) else: fo.write(line) fo.write(''' def suite(): return unittest.TestLoader().loadTestsFromTestCase(%sTestCase) if __name__ == '__main__': unittest.main() ''' % os.path.basename(base)) fo.close() fd.close() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/fullcheck.sh����������������������������������������������������������������������0000775�0000000�0000000�00000001016�14411236400�0016230�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh VALGRIND='' if [ -x /usr/bin/valgrind -o -x /opt/local/bin/valgrind ]; then VALGRIND="valgrind -q --track-origins=yes" if [ `uname` = "Darwin" ]; then VALGRIND="$VALGRIND --dsymutil=yes" fi fi #export MallocGuardEdges=1 #export MallocScribble=1 #export MallocPreScribble=1 #export MallocCheckHeapStart=100 #export MallocCheckHeapEach=100 #export DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib #export MALLOC_PROTECT_BEFORE=1 #export MALLOC_FILL_SPACE=1 #export MALLOC_STRICT_SIZE=1 exec $VALGRIND $@ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/input/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0015072�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/input/demo.ledger�����������������������������������������������������������������0000664�0000000�0000000�00000003351�14411236400�0017204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2010/12/01 * Checking balance Assets:Checking $1,000.00 Equity:Opening Balances 2010/12/20 * Organic Co-op Expenses:Food:Groceries $ 37.50 ; [=2011/01/01] Expenses:Food:Groceries $ 37.50 ; [=2011/02/01] Expenses:Food:Groceries $ 37.50 ; [=2011/03/01] Expenses:Food:Groceries $ 37.50 ; [=2011/04/01] Expenses:Food:Groceries $ 37.50 ; [=2011/05/01] Expenses:Food:Groceries $ 37.50 ; [=2011/06/01] Assets:Checking $ -225.00 2010/12/28 Acme Mortgage Liabilities:Mortgage:Principal $ 200.00 Expenses:Interest:Mortgage $ 500.00 Expenses:Escrow $ 300.00 * Assets:Checking $ -1000.00 2011/01/02 Grocery Store Expenses:Food:Groceries $ 65.00 * Assets:Checking 2011/01/05 Employer * Assets:Checking $ 2000.00 Income:Salary 2011/01/14 Bank ; Regular monthly savings transfer Assets:Savings $ 300.00 Assets:Checking 2011/01/19 Grocery Store Expenses:Food:Groceries $ 44.00 ; hastag: not block Assets:Checking 2011/01/25 Bank ; Transfer to cover car purchase Assets:Checking $ 5,500.00 Assets:Savings ; :nobudget: 2011/01/25 Tom's Used Cars Expenses:Auto $ 5,500.00 ; :nobudget: Assets:Checking 2011/01/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard 2011/04/25 Tom's Used Cars Expenses:Auto $ 5,500.00 ; :nobudget: Assets:Checking 2011/04/27 Bookstore Expenses:Books $20.00 Assets:Checking 2011/12/01 Sale Assets:Checking $ 30.00 Income:Sales ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/input/divzero.dat�����������������������������������������������������������������0000664�0000000�0000000�00000000767�14411236400�0017260�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; -*- ledger -*- ; Time-stamp: <2011-11-22 13:59 (cpearls)> ; this journal file will cause a divide by zero error. The divide by ; zero goes away if you add a significant figure to any of the last ; divisors in either transaction ( "10.74" -> "10.740" ) 2011/04/05 * VSGBX Dividend Assets:Investments:401K:Matching ( (1.0/3.0)*0.11/10.74 VSGBX) ; 0.003414 Assets:Investments:401K:Deferred ( (2.0/3.0)*0.11/10.74 VSGBX) ; 0.006828 Income:Exempt:Dividends $-0.11 ���������ledger-3.3.2/test/input/drewr.dat�������������������������������������������������������������������0000664�0000000�0000000�00000003271�14411236400�0016712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; -*- ledger -*- = /^Income/ (Liabilities:Tithe) 0.12 ~ monthly in 2004 Assets:Checking $500.00 Income:Salary 2003/12/01 * Checking balance Assets:Checking $1,000.00 Equity:Opening Balances 2003/12/20 Organic Co-op Expenses:Food:Groceries $ 37.50 ; [=2004/01/01] Expenses:Food:Groceries $ 37.50 ; [=2004/02/01] Expenses:Food:Groceries $ 37.50 ; [=2004/03/01] Expenses:Food:Groceries $ 37.50 ; [=2004/04/01] Expenses:Food:Groceries $ 37.50 ; [=2004/05/01] Expenses:Food:Groceries $ 37.50 ; [=2004/06/01] Assets:Checking $ -225.00 2003/12/28=2004/01/01 Acme Mortgage Liabilities:Mortgage:Principal $ 200.00 Expenses:Interest:Mortgage $ 500.00 Expenses:Escrow $ 300.00 Assets:Checking $ -1000.00 2004/01/02 Grocery Store Expenses:Food:Groceries $ 65.00 Assets:Checking 2004/01/05 Employer Assets:Checking $ 2000.00 Income:Salary 2004/01/14 Bank ; Regular monthly savings transfer Assets:Savings $ 300.00 Assets:Checking 2004/01/19 Grocery Store Expenses:Food:Groceries $ 44.00 Assets:Checking 2004/01/25 Bank ; Transfer to cover car purchase Assets:Checking $ 5,500.00 Assets:Savings ; :nobudget: 2004/01/25 Tom's Used Cars Expenses:Auto $ 5,500.00 ; :nobudget: Assets:Checking 2004/01/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard 2004/02/01 Sale Assets:Checking:Business $ 30.00 Income:Sales ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/input/drewr3.dat������������������������������������������������������������������0000664�0000000�0000000�00000003473�14411236400�0017001�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; -*- ledger -*- = /^Income/ (Liabilities:Tithe) 0.12 ;~ Monthly ; Assets:Checking $500.00 ; Income:Salary ;~ Monthly ; Expenses:Food $100 ; Assets 2010/12/01 * Checking balance Assets:Checking $1,000.00 Equity:Opening Balances 2010/12/20 * Organic Co-op Expenses:Food:Groceries $ 37.50 ; [=2011/01/01] Expenses:Food:Groceries $ 37.50 ; [=2011/02/01] Expenses:Food:Groceries $ 37.50 ; [=2011/03/01] Expenses:Food:Groceries $ 37.50 ; [=2011/04/01] Expenses:Food:Groceries $ 37.50 ; [=2011/05/01] Expenses:Food:Groceries $ 37.50 ; [=2011/06/01] Assets:Checking $ -225.00 2010/12/28=2011/01/01 Acme Mortgage Liabilities:Mortgage:Principal $ 200.00 Expenses:Interest:Mortgage $ 500.00 Expenses:Escrow $ 300.00 Assets:Checking $ -1000.00 2011/01/02 Grocery Store Expenses:Food:Groceries $ 65.00 Assets:Checking 2011/01/05 Employer Assets:Checking $ 2000.00 Income:Salary 2011/01/14 Bank ; Regular monthly savings transfer Assets:Savings $ 300.00 Assets:Checking 2011/01/19 Grocery Store Expenses:Food:Groceries $ 44.00 ; hastag: not block Assets:Checking 2011/01/25 Bank ; Transfer to cover car purchase Assets:Checking $ 5,500.00 Assets:Savings ; :nobudget: apply tag hastag: true apply tag nestedtag: true 2011/01/25 Tom's Used Cars Expenses:Auto $ 5,500.00 ; :nobudget: Assets:Checking 2011/01/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard end tag 2011/12/01 Sale Assets:Checking:Business $ 30.00 Income:Sales end tag �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/input/parsing.dat�����������������������������������������������������������������0000664�0000000�0000000�00000000212�14411236400�0017222�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ;NOTE: this file should NOT end in a new line ;See https://github.com/ledger/ledger/issues/516 2021/07/14 test Assets $30 Income -$30��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/input/sample.dat������������������������������������������������������������������0000664�0000000�0000000�00000002764�14411236400�0017056�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; -*- ledger -*- N $ = /^Expenses:Books/ (Liabilities:Taxes) -0.10 ~ Monthly Assets:Bank:Checking $500.00 Income:Salary ~ Yearly Expenses:Donations $100.00 Assets:Bank:Checking 2004/05/01 * Checking balance Assets:Bank:Checking $1,000.00 Equity:Opening Balances 2004/05/03=2004/05/01 * Investment balance Assets:Brokerage 50 AAPL @ $30.00 Equity:Opening Balances 2004/05/14 * Páy dày Assets:Bank:Checking 500.00€ Income:Salary 2004/05/14 * Another dày in which there is Páying Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00 Income:Salary 2004/05/14 * Another dày in which there is Páying Русский язык:Активы:Русский язык:Русский язык $1000.00 Income:Salary apply tag foo 2004/05/27 Book Store Expenses:Books $20.00 Expenses:Cards $40.00 Expenses:Docs $30.00 Liabilities:MasterCard end apply tag 2004/05/27 (100) Credit card company ; This is an xact note! ; Sample: Value Liabilities:MasterCard $20.00 ; This is a posting note! ; Sample: Another Value ; :MyTag: Assets:Bank:Checking ; :AnotherTag: ;;; sample.dat ends here ������������ledger-3.3.2/test/input/standard.dat����������������������������������������������������������������0000664�0000000�0000000�00000743256�14411236400�0017405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2002/01/01 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be af0628973ff35bd62ddb048fa41dd8d83c1c46fe $474.31 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/03/01 * 9861ce541c17b11f627e71c26bf350b33141f62b 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $14.91 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/03/09 * 4891c3f867e3d91808f83da388a5ba439aedba80 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $1,173.15 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/03/09 * 2fb83b483eeab9d98529783900fa85eb4348d991 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $693.23 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/03/09 * 20bc87fd47b41f9abf6815df7496e90345f7ed37 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $64.00 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/05/18 * 4891c3f867e3d91808f83da388a5ba439aedba80 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $481.25 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/05/18 * 2fb83b483eeab9d98529783900fa85eb4348d991 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $357.93 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/09/14 * 20bc87fd47b41f9abf6815df7496e90345f7ed37 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $32.00 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/09/14 * 2fb83b483eeab9d98529783900fa85eb4348d991 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $338.83 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/09/14 * 4891c3f867e3d91808f83da388a5ba439aedba80 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $481.25 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/09/30 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be c56a21d23a6535184e7152ee138c28974f14280c 866.231000 GGGGG a35e82730cf91569c302b313780e5895f75a62b9 $-17,783.72 2002/09/30 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be c56a21d23a6535184e7152ee138c28974f14280c 1,925.940000 AAAAA a35e82730cf91569c302b313780e5895f75a62b9 $-33,299.47 2002/09/30 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be c56a21d23a6535184e7152ee138c28974f14280c 564.042000 EEEEE a35e82730cf91569c302b313780e5895f75a62b9 $-15,810.10 2002/10/01 * db16aa488f49561098cd08c4749d94256c733b64 fc6f6f10f627ad1a5af9d488c98405a1498d019d $450.00 11c48bb7aa6231a23d96299904885620d9fb3b1a 2002/10/01 * d11efaa25b28e9aa5bd7b020f70f161cb43d3e7d 11c48bb7aa6231a23d96299904885620d9fb3b1a $900.00 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/10/18 * 4891c3f867e3d91808f83da388a5ba439aedba80 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $343.75 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/10/19 * b007369e15aba78cb6075310da96b854f5448a3a 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $17.59 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/10/20 * 4891c3f867e3d91808f83da388a5ba439aedba80 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $137.50 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/10/25 * 2fb83b483eeab9d98529783900fa85eb4348d991 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $589.40 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/10/25 * 4891c3f867e3d91808f83da388a5ba439aedba80 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $354.36 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/10/27 * (2031) d1704e602da55041cc9c5f83a1076b1551c1225a 11c48bb7aa6231a23d96299904885620d9fb3b1a $900.00 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/10/31 * db16aa488f49561098cd08c4749d94256c733b64 fc6f6f10f627ad1a5af9d488c98405a1498d019d $450.00 11c48bb7aa6231a23d96299904885620d9fb3b1a 2002/11/30 * db16aa488f49561098cd08c4749d94256c733b64 fc6f6f10f627ad1a5af9d488c98405a1498d019d $450.00 11c48bb7aa6231a23d96299904885620d9fb3b1a 2002/12/17 * f94418bf56f6656f43bac8f2b9bf4ce940614f44 0a014a93e9bf8b2b56afd4ffeeeca7da7d3af3fd $39.90 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2002/12/22 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be 7826c9ce60ae644a02466043232f592994802448 82.288 CCCCC a35e82730cf91569c302b313780e5895f75a62b9 $-1,465.55 2002/12/22 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be c0226fafdf9e6711ac9121cf263e2d50791859cb 1,012.251 CCCCC a35e82730cf91569c302b313780e5895f75a62b9 $-18,028.19 2002/12/24 * fa73d67b17a393d9db8c96a9f3ec222804fe0fda 9c484b5dc87055f93751ad00947fd9a7a14ea470 $128.45 f0d45665b22d0562833aa3bf373c5b15640d833e $64.22 4907823cffe667ad9decdcdd3a4780c15485c6ea $-128.45 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $-64.22 2002/12/25 * 7592910fc29b651a46c7c700406ed51978ae4cd6 f0d45665b22d0562833aa3bf373c5b15640d833e $11.53 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2002/12/28 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 f0d45665b22d0562833aa3bf373c5b15640d833e $11.68 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2002/12/30 * 580e3cdf2e29864c0c1abfde600494a83263c68a f0d45665b22d0562833aa3bf373c5b15640d833e $15.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2002/12/31 * 32fcd74c867c3ddbff9b8d38e8294d6727b1f3a2 e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $20.00 88671479b5cf1fbd5df40139835b336f10e4c7a1 2002/12/31 * db16aa488f49561098cd08c4749d94256c733b64 463628a20f371d71d46a7947f1175a0c16ce2f45 $450.00 11c48bb7aa6231a23d96299904885620d9fb3b1a 2002/12/31 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be 15d4990b82d33262ff733b3e6539d66a0445c193 $1,200.04 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/12/31 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be fc6f6f10f627ad1a5af9d488c98405a1498d019d $484.29 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2002/12/31 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be fc0e191163be4d1966e3c51b1635401f9e82a807 $13,692.31 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/12/31 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be fc6f6f10f627ad1a5af9d488c98405a1498d019d $82,589.97 f0d45665b22d0562833aa3bf373c5b15640d833e 2002/12/31 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be (845ac5d9910830a5764c934bf791195b0fcd91f4) $-18,384.85 2002/12/31 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $7,650.70 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2002/12/31 * (2097) 098d6e0cbcd5aebfbcdb912993e2d789e30f3a81 3e2706db92ca6bb952333fd028e582695910c01d $55.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/01 * 95b6b46bf9f262cf7aeffa04ba12d3bde9411eb6 1b565047893eb8f55e839a9f0b5259d047547a82 $87.21 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/01 * b7d9ccca2f575498c7c0e626d96c97b2735ee398 fc6f6f10f627ad1a5af9d488c98405a1498d019d $3,000.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/01 * 8c40cce6f07a195ac21076a3f150035c65264aa8 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $77,589.97 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2003/01/01 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $1,812.50 15d4990b82d33262ff733b3e6539d66a0445c193 $600.02 677639d3e48c3ac2413f12c1a3e6b67525e09009 $539.70 4fd4e6978513bf18a906891e8e8c4b307ae3565e $245.00 a64166a90252d444071c62e9e0746ce6e83234b6 $57.30 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $97.15 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $48.46 c56a21d23a6535184e7152ee138c28974f14280c $600.02 (845ac5d9910830a5764c934bf791195b0fcd91f4) $114.00 39189083b8637c7fff89e6bcf808790861417796 $-4,000.15 2003/01/01 * 91361769cb74ddd9f9fc545ca4f1bc4e4d7d7212 c56a21d23a6535184e7152ee138c28974f14280c $120.01 775a73f9b8633e396be7cfd03b6a46f07b47c5ef 2003/01/01 * (2089) e48c080a9f967951944a3ecd5d6e6bf1d296725f 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $21.51 f0d45665b22d0562833aa3bf373c5b15640d833e $21.51 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-43.02 2003/01/01 * (2098) d1704e602da55041cc9c5f83a1076b1551c1225a 11c48bb7aa6231a23d96299904885620d9fb3b1a $450.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/01 * 04ff4313e11dca88fb1707316329ff143f8f78c2 f0d45665b22d0562833aa3bf373c5b15640d833e $19.01 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/02 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be 64feb5a551f10cc181b37cc3af9d95c82e48a916 $5.00 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2003/01/02 * c724d9c91df5cb3bd4441a2cd9e15676c61555f7 f0d45665b22d0562833aa3bf373c5b15640d833e $30.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/02 * c724d9c91df5cb3bd4441a2cd9e15676c61555f7 f0d45665b22d0562833aa3bf373c5b15640d833e $367.75 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/02 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 f0d45665b22d0562833aa3bf373c5b15640d833e $36.16 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/02 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 f0d45665b22d0562833aa3bf373c5b15640d833e $12.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/02 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 5.838000 EEEEE c56a21d23a6535184e7152ee138c28974f14280c $-180.00 2003/01/02 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 8.262000 GGGGG c56a21d23a6535184e7152ee138c28974f14280c $-180.02 2003/01/02 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 18.930000 AAAAA c56a21d23a6535184e7152ee138c28974f14280c $-360.01 2003/01/03 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $10.16 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-60.16 2003/01/04 * 268fe8c09361043234b89aa2d55fd25091b851e1 f0d45665b22d0562833aa3bf373c5b15640d833e $4.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/04 * e77f5f8a743363bfb4938ddfd5f7ae8f5b370fad f0d45665b22d0562833aa3bf373c5b15640d833e $12.06 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/05 * (2099) 74f61fee296e715055dc0f5ae30804ae450fb7dc 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $100,000.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/05 * 32c8ced97c0cde788ffc072673579337197ecc71 fc0e191163be4d1966e3c51b1635401f9e82a807 $8,210.09 677639d3e48c3ac2413f12c1a3e6b67525e09009 $3,400.62 cecae7f2312046d2775a401cc3c3925b79676ce3 $20.68 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $1,552.69 a64166a90252d444071c62e9e0746ce6e83234b6 $182.63 4fd4e6978513bf18a906891e8e8c4b307ae3565e $780.89 ef4f24d74e0801474c3d039fbb4df9ebecd5de52 $-12,594.91 f0d45665b22d0562833aa3bf373c5b15640d833e $-1,552.69 2003/01/05 * e65f77390d2ff7c7040d6fed1a6b62d5ba027748 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $200.00 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $100.00 af0628973ff35bd62ddb048fa41dd8d83c1c46fe $-200.00 f0d45665b22d0562833aa3bf373c5b15640d833e $-100.00 2003/01/05 * c94f479833c5d401cffdfa7afe6c9c2d56448019 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $21.94 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2003/01/05 * 504c6c141d7ed2aa8a8a8786be2225c39cee7a0f 88671479b5cf1fbd5df40139835b336f10e4c7a1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/05 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $18.61 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/05 * (2100) 7994a463ade0f972bf1bb59ac236e37b08bfc4d7 904eafc4a3d3e7ee665de39f7baa66d9785b3c98 $178.67 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/05 * (2101) 39edcef4400a8d5b613da37c354870d87f6b60b8 cecae7f2312046d2775a401cc3c3925b79676ce3 $15.00 f0d45665b22d0562833aa3bf373c5b15640d833e $7.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-15.00 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $-7.50 2003/01/05 * (2102) 03642d4c8cfd39cbe8e1bbce3179a0ca53935e30 f0d45665b22d0562833aa3bf373c5b15640d833e $1,500.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/06 * 8c40cce6f07a195ac21076a3f150035c65264aa8 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $2,537.79 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2003/01/06 * 95b6b46bf9f262cf7aeffa04ba12d3bde9411eb6 1b565047893eb8f55e839a9f0b5259d047547a82 $128.91 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/06 * 8c40cce6f07a195ac21076a3f150035c65264aa8 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $103,789.94 fc6f6f10f627ad1a5af9d488c98405a1498d019d 2003/01/07 * 6f3e9ddd855e82d1ec25ed14b2180d34b08f1045 f0d45665b22d0562833aa3bf373c5b15640d833e $26.19 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/07 * eacc3fc8d502d7f4e62f04f1325b9f5f463cc387 f0d45665b22d0562833aa3bf373c5b15640d833e $3.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/07 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $9.75 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/07 * (2095) 03642d4c8cfd39cbe8e1bbce3179a0ca53935e30 f0d45665b22d0562833aa3bf373c5b15640d833e $82,589.97 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/08 * (2103) 35b7ad4a355641a58d7ef29cce5ce8dadc550d35 cecae7f2312046d2775a401cc3c3925b79676ce3 $190.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/09 * fa39c23bd7d06b52f53d88060f6522ed961cc7e8 f0d45665b22d0562833aa3bf373c5b15640d833e $12.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/09 * b1b4091f86e761f140374909aeb031bfe4ddf404 3282f21c97a0e1f66185923328d80d87fa5d8db7 $19.54 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/10 * 028eaa2ba85404a279ea2978142c687b0a75dff3 1b565047893eb8f55e839a9f0b5259d047547a82 $11.45 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/10 * 65646e6c930f4d0d52f63a610acf04138d855930 6d93163434f5b2253b3b5283f6015b60c79ea659 $16.14 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/11 * 0fb419d273cdd0cfe9825e94f91c2058e99c1f7a 1b565047893eb8f55e839a9f0b5259d047547a82 $33.41 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/12 * 38be5d47bdf3f41c49d0d4bd5cce5fa442098349 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $32.93 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $16.46 e8a7204939bd995e0343c42de4366ad4fb95fe4d $-32.93 f0d45665b22d0562833aa3bf373c5b15640d833e $-16.46 2003/01/12 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $137.74 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/01/12 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $14.01 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-34.01 2003/01/12 * 405295e72fc5fb41cb97a68a217108b08b4e045e 1b565047893eb8f55e839a9f0b5259d047547a82 $3.78 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/12 * 8f7e593a4e05c2cf160d61d4a8b556368f0d7818 fc0e191163be4d1966e3c51b1635401f9e82a807 1,558.89 BRSIX fc0e191163be4d1966e3c51b1635401f9e82a807 $-13,500.00 2003/01/12 * (2096) fa73d67b17a393d9db8c96a9f3ec222804fe0fda 4907823cffe667ad9decdcdd3a4780c15485c6ea $128.45 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/13 * 31eeb3d4b0873efdd0c81d86a5f1cc13585d192f 6d93163434f5b2253b3b5283f6015b60c79ea659 $23.63 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/13 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $21.41 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-71.41 2003/01/13 * 4c0edf908955202437646c30421cdd92f4fe3088 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $75.00 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/01/14 * a46af6931d9dace2200617548fab3274549e308f 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $75.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/14 * (2104) fa73d67b17a393d9db8c96a9f3ec222804fe0fda 4907823cffe667ad9decdcdd3a4780c15485c6ea $105.17 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/15 * 31eeb3d4b0873efdd0c81d86a5f1cc13585d192f 6d93163434f5b2253b3b5283f6015b60c79ea659 $16.12 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/15 * (2106) 619cbfd21d72639f985324a9306696ce1262f3f6 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $5,000.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/15 * bf5312fa21eac164606692125d69af404878faa1 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $50.00 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2003/01/15 * (2105) 4ffc4349510313ea639fc1e50d7e156c456cdb84 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $60.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/15 * bea8d5e2678e516790a1d65f5c22875c1ee81f09 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $100.00 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2003/01/15 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $1,812.49 15d4990b82d33262ff733b3e6539d66a0445c193 $600.02 677639d3e48c3ac2413f12c1a3e6b67525e09009 $539.70 4fd4e6978513bf18a906891e8e8c4b307ae3565e $245.01 a64166a90252d444071c62e9e0746ce6e83234b6 $57.30 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $97.15 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $48.46 c56a21d23a6535184e7152ee138c28974f14280c $600.02 (845ac5d9910830a5764c934bf791195b0fcd91f4) $114.00 39189083b8637c7fff89e6bcf808790861417796 $-4,000.15 2003/01/15 * 91361769cb74ddd9f9fc545ca4f1bc4e4d7d7212 c56a21d23a6535184e7152ee138c28974f14280c $120.01 775a73f9b8633e396be7cfd03b6a46f07b47c5ef 2003/01/15 * f50bc0cf6300861a90dc5b29443cd614a09ffd88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $484.29 f0d45665b22d0562833aa3bf373c5b15640d833e $242.14 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-484.29 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $-242.14 2003/01/16 * 2fac90ee40cd30ece1ad465f69e2d84d5ae64a9c fc0e191163be4d1966e3c51b1635401f9e82a807 $3.79 e8a7204939bd995e0343c42de4366ad4fb95fe4d 2003/01/16 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 5.871000 EEEEE c56a21d23a6535184e7152ee138c28974f14280c $-180.00 2003/01/16 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 18.970000 AAAAA c56a21d23a6535184e7152ee138c28974f14280c $-360.01 2003/01/16 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 8.389000 GGGGG c56a21d23a6535184e7152ee138c28974f14280c $-180.02 2003/01/16 * 6045e146184c24b57b23831ab235fe447eb5b221 181884ada86d2c3e7511e3ef3830fcf9f75b1421 $11.40 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/17 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $24.02 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-44.02 2003/01/18 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $20.44 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/18 * 8c959e2ec53ec728cb1dec19971a950359c82e02 ca268d538b1a0056c1e3c8c5874d4cb30452d738 $40.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/19 * fa73d67b17a393d9db8c96a9f3ec222804fe0fda 9c484b5dc87055f93751ad00947fd9a7a14ea470 $105.17 4907823cffe667ad9decdcdd3a4780c15485c6ea 2003/01/20 * (101) d65a386cd0aaed34259bb2207516b1f81a0cb53c 3baf6f27bea8f4639c5e61228227dc8bfa2c46a5 $25,000.00 (845ac5d9910830a5764c934bf791195b0fcd91f4) $25,000.00 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 2003/01/21 * 856c722d0a5c00b903ab4d7c2f93505edb13bb93 1b565047893eb8f55e839a9f0b5259d047547a82 $19.71 cfd76529eda7575c434ab6edd70e56693f979bb1 $35.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-54.71 2003/01/22 * 8f7e593a4e05c2cf160d61d4a8b556368f0d7818 fc0e191163be4d1966e3c51b1635401f9e82a807 -1,558.89 BRSIX fc0e191163be4d1966e3c51b1635401f9e82a807 $13,281.74 2003/01/24 * 42939113edd815fec10f655034af3c7864bcb951 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/24 * 1e49e75ca9266dcd066c3f935f6cf6c6156f97c7 1b565047893eb8f55e839a9f0b5259d047547a82 $20.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/25 * a0cdc203f150cf807d3fc44540ccb28e6270e47a 6d93163434f5b2253b3b5283f6015b60c79ea659 $8.34 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/27 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $2.25 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-102.25 2003/01/27 * c820efeab420f1943da93ef6a80fa5867d2cce68 3282f21c97a0e1f66185923328d80d87fa5d8db7 $22.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/29 * 6e86042aa3127e49456b188a9c8d7c41f8c017d4 fc0e191163be4d1966e3c51b1635401f9e82a807 $0.73 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/01/29 * 625a740024b74b8dd4d98b56b5efe171f3f5c9cc cecae7f2312046d2775a401cc3c3925b79676ce3 $265.64 fc0e191163be4d1966e3c51b1635401f9e82a807 2003/01/29 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $1,811.55 15d4990b82d33262ff733b3e6539d66a0445c193 $600.02 677639d3e48c3ac2413f12c1a3e6b67525e09009 $539.70 4fd4e6978513bf18a906891e8e8c4b307ae3565e $245.79 a64166a90252d444071c62e9e0746ce6e83234b6 $57.46 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $97.15 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $48.46 c56a21d23a6535184e7152ee138c28974f14280c $600.02 (845ac5d9910830a5764c934bf791195b0fcd91f4) $114.00 39189083b8637c7fff89e6bcf808790861417796 $-4,000.15 2003/01/29 * 91361769cb74ddd9f9fc545ca4f1bc4e4d7d7212 c56a21d23a6535184e7152ee138c28974f14280c $120.01 775a73f9b8633e396be7cfd03b6a46f07b47c5ef 2003/01/30 * 5030f5a1c32cc3fa29ad77aefd16fa3a8ed3c666 0a014a93e9bf8b2b56afd4ffeeeca7da7d3af3fd $13.45 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/01/30 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 8.764000 GGGGG c56a21d23a6535184e7152ee138c28974f14280c $-180.02 2003/01/30 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 19.890000 AAAAA c56a21d23a6535184e7152ee138c28974f14280c $-360.01 2003/01/30 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 6.164000 EEEEE c56a21d23a6535184e7152ee138c28974f14280c $-180.00 2003/01/30 * 2fac90ee40cd30ece1ad465f69e2d84d5ae64a9c fc0e191163be4d1966e3c51b1635401f9e82a807 $1.98 e8a7204939bd995e0343c42de4366ad4fb95fe4d 2003/01/30 * 32c8ced97c0cde788ffc072673579337197ecc71 fc0e191163be4d1966e3c51b1635401f9e82a807 $425.81 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $1,898.27 677639d3e48c3ac2413f12c1a3e6b67525e09009 $1,571.69 cecae7f2312046d2775a401cc3c3925b79676ce3 $19.95 a64166a90252d444071c62e9e0746ce6e83234b6 $84.41 4fd4e6978513bf18a906891e8e8c4b307ae3565e $360.91 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $3,358.30 ef4f24d74e0801474c3d039fbb4df9ebecd5de52 $-5,821.07 f0d45665b22d0562833aa3bf373c5b15640d833e $-1,898.27 2003/01/30 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $12.69 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/01/31 * 32fcd74c867c3ddbff9b8d38e8294d6727b1f3a2 e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $20.00 88671479b5cf1fbd5df40139835b336f10e4c7a1 2003/01/31 * db16aa488f49561098cd08c4749d94256c733b64 463628a20f371d71d46a7947f1175a0c16ce2f45 $450.00 11c48bb7aa6231a23d96299904885620d9fb3b1a 2003/02/01 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $13.84 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/02 * 3448304b899a638ade73b30d085269b8c8651c8d 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $16,450.00 fc0e191163be4d1966e3c51b1635401f9e82a807 2003/02/03 * (2107) a4c1053a2870f13b1bfdd38db80b5d72acfba4d2 c233d176ce06c06ecfd032230c4be5ff4476a554 $55.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/03 * bce0a909463614ce1a6b6b8dc4ae66a3cf232086 6d93163434f5b2253b3b5283f6015b60c79ea659 $11.47 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/03 * c50ffe9fd9ab159d36900b2a90d0db4ddf70311e 10cab4f7105feed78ca34f97ba79d013cb8e28f4 $8.61 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/03 * (2108) d1704e602da55041cc9c5f83a1076b1551c1225a 11c48bb7aa6231a23d96299904885620d9fb3b1a $450.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/05 * 504c6c141d7ed2aa8a8a8786be2225c39cee7a0f 88671479b5cf1fbd5df40139835b336f10e4c7a1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/06 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $14.95 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/06 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $2.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-102.00 2003/02/07 * 38bf98a7f1e072558f56cd895247f2b03b533559 1b565047893eb8f55e839a9f0b5259d047547a82 $12.21 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/07 * fa39c23bd7d06b52f53d88060f6522ed961cc7e8 3282f21c97a0e1f66185923328d80d87fa5d8db7 $22.44 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/07 * 1b0cfd69357b7c20fb55adfc8fe5afa6ddffe80a 6d93163434f5b2253b3b5283f6015b60c79ea659 $35.49 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/09 * 222db0fc1cadf0af4ccdbccb7ecbb0aa86efb2fe 9e67321982e83628563e8a2b396325aa18283ba5 $26.13 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/09 * c7766c8fa1a0a32c2c7f3a3fefbb0840e0802cca 1b565047893eb8f55e839a9f0b5259d047547a82 $6.20 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/09 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $22.33 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/09 * 3448304b899a638ade73b30d085269b8c8651c8d 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $5,400.00 fc0e191163be4d1966e3c51b1635401f9e82a807 2003/02/09 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad fc0e191163be4d1966e3c51b1635401f9e82a807 $0.49 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/02/10 * (2109) a4c1053a2870f13b1bfdd38db80b5d72acfba4d2 c233d176ce06c06ecfd032230c4be5ff4476a554 $58.80 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/11 * 606d337268d95afe2603939a623db0813eba2651 9e67321982e83628563e8a2b396325aa18283ba5 $234.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/12 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $1,812.49 15d4990b82d33262ff733b3e6539d66a0445c193 $600.02 677639d3e48c3ac2413f12c1a3e6b67525e09009 $539.70 4fd4e6978513bf18a906891e8e8c4b307ae3565e $245.01 a64166a90252d444071c62e9e0746ce6e83234b6 $57.30 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $97.15 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $48.46 c56a21d23a6535184e7152ee138c28974f14280c $600.02 (845ac5d9910830a5764c934bf791195b0fcd91f4) $114.00 39189083b8637c7fff89e6bcf808790861417796 $-4,000.15 2003/02/12 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $1,560.56 15d4990b82d33262ff733b3e6539d66a0445c193 $514.59 677639d3e48c3ac2413f12c1a3e6b67525e09009 $926.25 4fd4e6978513bf18a906891e8e8c4b307ae3565e $212.69 a64166a90252d444071c62e9e0746ce6e83234b6 $49.75 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $166.73 e49713c1693a3eeddb9cef202706e9d1a44cc481 $-3,430.57 2003/02/12 * 91361769cb74ddd9f9fc545ca4f1bc4e4d7d7212 c56a21d23a6535184e7152ee138c28974f14280c $120.01 775a73f9b8633e396be7cfd03b6a46f07b47c5ef 2003/02/12 * d2667e42a8beac84d6f95887ab807a947a425d67 1b565047893eb8f55e839a9f0b5259d047547a82 $48.51 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/13 * 31eeb3d4b0873efdd0c81d86a5f1cc13585d192f 9e67321982e83628563e8a2b396325aa18283ba5 $17.20 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/15 * 0f4b0afecf3d935e7bc810e1e0920cc8ee6f15ec c233d176ce06c06ecfd032230c4be5ff4476a554 $64.56 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/16 * f50bc0cf6300861a90dc5b29443cd614a09ffd88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $603.31 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/16 * bea8d5e2678e516790a1d65f5c22875c1ee81f09 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $100.00 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2003/02/16 * (2110) a4c1053a2870f13b1bfdd38db80b5d72acfba4d2 c233d176ce06c06ecfd032230c4be5ff4476a554 $19.60 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/16 * (2111) fa73d67b17a393d9db8c96a9f3ec222804fe0fda 4907823cffe667ad9decdcdd3a4780c15485c6ea $132.77 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/16 * (2112) 7994a463ade0f972bf1bb59ac236e37b08bfc4d7 904eafc4a3d3e7ee665de39f7baa66d9785b3c98 $119.12 628072f9bfc87daeb5e0144ef2090ef8057935e8 $151.80 e8a7204939bd995e0343c42de4366ad4fb95fe4d $-68.03 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-202.89 2003/02/17 * fa73d67b17a393d9db8c96a9f3ec222804fe0fda 9c484b5dc87055f93751ad00947fd9a7a14ea470 $132.77 4907823cffe667ad9decdcdd3a4780c15485c6ea 2003/02/17 * 0f4b0afecf3d935e7bc810e1e0920cc8ee6f15ec c233d176ce06c06ecfd032230c4be5ff4476a554 $10.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/17 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 6.218000 EEEEE c56a21d23a6535184e7152ee138c28974f14280c $-180.00 2003/02/17 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 20.020000 AAAAA c56a21d23a6535184e7152ee138c28974f14280c $-360.01 2003/02/17 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 8.803000 GGGGG c56a21d23a6535184e7152ee138c28974f14280c $-180.02 2003/02/19 * 0f4b0afecf3d935e7bc810e1e0920cc8ee6f15ec c233d176ce06c06ecfd032230c4be5ff4476a554 $23.25 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/19 * 353483ad2a505c5f08dc1dc306993f394602c536 c233d176ce06c06ecfd032230c4be5ff4476a554 $13.95 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/19 * 00115450eb0f8723f959072daa56a87a9aeddfee 1b565047893eb8f55e839a9f0b5259d047547a82 $11.83 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/19 * (102) bfe57c24b1164278d38739f81ca886131f8786aa 2ff50cab09d039eaa717cac29cf2759eb39a007d $1,500.00 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 2003/02/20 * e65f77390d2ff7c7040d6fed1a6b62d5ba027748 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $274.31 af0628973ff35bd62ddb048fa41dd8d83c1c46fe 2003/02/23 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-101.50 2003/02/23 * e4811328450a9391ead85ddbcf2f25cda2e4ae2a 1b565047893eb8f55e839a9f0b5259d047547a82 $9.16 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/24 * 288db0acb7096d8b553f3c77c9ef888a6e8ac5df 1b565047893eb8f55e839a9f0b5259d047547a82 $42.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/25 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.40 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/25 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $1,811.52 15d4990b82d33262ff733b3e6539d66a0445c193 $600.02 677639d3e48c3ac2413f12c1a3e6b67525e09009 $539.70 4fd4e6978513bf18a906891e8e8c4b307ae3565e $245.80 a64166a90252d444071c62e9e0746ce6e83234b6 $57.48 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $97.15 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $48.46 c56a21d23a6535184e7152ee138c28974f14280c $600.02 (845ac5d9910830a5764c934bf791195b0fcd91f4) $114.00 39189083b8637c7fff89e6bcf808790861417796 $-4,000.15 2003/02/25 * 91361769cb74ddd9f9fc545ca4f1bc4e4d7d7212 c56a21d23a6535184e7152ee138c28974f14280c $120.01 775a73f9b8633e396be7cfd03b6a46f07b47c5ef 2003/02/26 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad fc0e191163be4d1966e3c51b1635401f9e82a807 $0.39 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/02/26 * d66f20839763a4d86071659cdcbebaa6020203c3 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $10.35 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/26 * f159c0ab1d78ac3ddcd8264eed66b66516c7dd09 1b565047893eb8f55e839a9f0b5259d047547a82 $5.86 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/02/27 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 8.846000 GGGGG c56a21d23a6535184e7152ee138c28974f14280c $-180.02 2003/02/27 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $167.36 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/02/27 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 20.050000 AAAAA c56a21d23a6535184e7152ee138c28974f14280c $-360.01 2003/02/27 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 6.276000 EEEEE c56a21d23a6535184e7152ee138c28974f14280c $-180.00 2003/02/27 * dc53165e7620d16c1e26d61168889930b913ec30 fc0e191163be4d1966e3c51b1635401f9e82a807 $1.53 e8a7204939bd995e0343c42de4366ad4fb95fe4d 2003/02/28 * 1e5b455c3e91eb68f0f5e2505fa17afee84b96bc 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $2.70 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/02/28 * 5a146cb991ecea075a567f997ef95edad7fc3a24 92a772d9a491a8c8f239d9148b979f1da7369480 $7.57 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/02/28 * db16aa488f49561098cd08c4749d94256c733b64 463628a20f371d71d46a7947f1175a0c16ce2f45 $450.00 11c48bb7aa6231a23d96299904885620d9fb3b1a 2003/02/28 * 32fcd74c867c3ddbff9b8d38e8294d6727b1f3a2 e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $20.00 88671479b5cf1fbd5df40139835b336f10e4c7a1 2003/02/28 * 69f2e19499724e3daa9e84f5c1a4142cfe98889c 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $340.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/02/28 * 20bc87fd47b41f9abf6815df7496e90345f7ed37 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $32.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/02/28 * ab77b70037475007e0bf5fb4a3bbb53e817f64dd 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $7.78 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/01 * (2114) 74f61fee296e715055dc0f5ae30804ae450fb7dc 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $4,500.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/01 * 619cbfd21d72639f985324a9306696ce1262f3f6 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $5,079.15 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 2003/03/01 * 0760a28df0204fe7687476a3d0692abea12019d0 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $400.00 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2003/03/01 * 5e5a42f9d5d96e9a5ce1bf6453a882644fa84c0b fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $2.02 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2003/03/01 * 48227adff9f10cd94a570d7c9b7f6fe62cb6f4cb 1b565047893eb8f55e839a9f0b5259d047547a82 $6.45 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/01 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $11.85 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-31.85 2003/03/01 * (2113) d1704e602da55041cc9c5f83a1076b1551c1225a 11c48bb7aa6231a23d96299904885620d9fb3b1a $450.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/02 * 2597cd5f96b6970b831a4f56c3c99335f549fb19 9e67321982e83628563e8a2b396325aa18283ba5 $42.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/02 * b007369e15aba78cb6075310da96b854f5448a3a c233d176ce06c06ecfd032230c4be5ff4476a554 $1.93 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/02 * 63d0a265712c9c53d03cd1bde96c16ea9b114b06 1b565047893eb8f55e839a9f0b5259d047547a82 $12.05 cfd76529eda7575c434ab6edd70e56693f979bb1 $10.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-22.05 2003/03/03 * (2115) a4c1053a2870f13b1bfdd38db80b5d72acfba4d2 c233d176ce06c06ecfd032230c4be5ff4476a554 $9.80 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/03 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $18.37 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/03 * 856c722d0a5c00b903ab4d7c2f93505edb13bb93 1b565047893eb8f55e839a9f0b5259d047547a82 $18.24 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/03 * b4fc2e8df9487473dcd996b2f84a238f6d20410b cecae7f2312046d2775a401cc3c3925b79676ce3 $10.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/03/04 * ab514b97e8f008682b50e6908a80a3fb024f8d82 cecae7f2312046d2775a401cc3c3925b79676ce3 $66.33 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/04 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $28.43 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/04 * 0a104d94003cbb20ed0d8d2d014b5af21eed594f cecae7f2312046d2775a401cc3c3925b79676ce3 $35.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/05 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df c233d176ce06c06ecfd032230c4be5ff4476a554 $4.38 6d93163434f5b2253b3b5283f6015b60c79ea659 $6.78 1b565047893eb8f55e839a9f0b5259d047547a82 $2.24 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-13.40 2003/03/05 * 504c6c141d7ed2aa8a8a8786be2225c39cee7a0f 88671479b5cf1fbd5df40139835b336f10e4c7a1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/05 * a95c40f310c47178537882d69dcfe6b8b1dcc080 9e67321982e83628563e8a2b396325aa18283ba5 $12.91 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/06 * 184d579b7e3f0d3158394da939e1fa3e9ec82df6 c0226fafdf9e6711ac9121cf263e2d50791859cb 3.729 CCCCC e8a7204939bd995e0343c42de4366ad4fb95fe4d $-60.75 2003/03/06 * 184d579b7e3f0d3158394da939e1fa3e9ec82df6 7826c9ce60ae644a02466043232f592994802448 0.303 CCCCC e8a7204939bd995e0343c42de4366ad4fb95fe4d $-4.94 2003/03/07 * 9683fba84d910707cd96a7afcc0ddbc0162ae030 1b565047893eb8f55e839a9f0b5259d047547a82 $18.04 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/07 * 299eb28835cafffe80dbb8398c478bd6b35b34bf 1b565047893eb8f55e839a9f0b5259d047547a82 $4.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/09 * 0b4f388e2291d712032d94565e5bb1d10ecdc240 1b565047893eb8f55e839a9f0b5259d047547a82 $2.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/10 * 299eb28835cafffe80dbb8398c478bd6b35b34bf 1b565047893eb8f55e839a9f0b5259d047547a82 $4.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/11 * (2116) fa73d67b17a393d9db8c96a9f3ec222804fe0fda 4907823cffe667ad9decdcdd3a4780c15485c6ea $96.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/12 * 9683fba84d910707cd96a7afcc0ddbc0162ae030 1b565047893eb8f55e839a9f0b5259d047547a82 $9.66 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/12 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $11.72 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-31.72 2003/03/12 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $1,832.04 15d4990b82d33262ff733b3e6539d66a0445c193 $600.02 677639d3e48c3ac2413f12c1a3e6b67525e09009 $548.42 4fd4e6978513bf18a906891e8e8c4b307ae3565e $247.01 a64166a90252d444071c62e9e0746ce6e83234b6 $57.77 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $98.72 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $16.15 c56a21d23a6535184e7152ee138c28974f14280c $600.02 (845ac5d9910830a5764c934bf791195b0fcd91f4) $114.00 39189083b8637c7fff89e6bcf808790861417796 $-4,000.15 2003/03/12 * 91361769cb74ddd9f9fc545ca4f1bc4e4d7d7212 c56a21d23a6535184e7152ee138c28974f14280c $120.01 775a73f9b8633e396be7cfd03b6a46f07b47c5ef 2003/03/12 * f94418bf56f6656f43bac8f2b9bf4ce940614f44 9e67321982e83628563e8a2b396325aa18283ba5 $64.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/13 * (2117) 9d2e018cb90d72f2e27099e6771f33a7821b755a c233d176ce06c06ecfd032230c4be5ff4476a554 $9.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/13 * 63d0a265712c9c53d03cd1bde96c16ea9b114b06 1b565047893eb8f55e839a9f0b5259d047547a82 $12.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/14 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.36 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-53.36 2003/03/14 * c78af9d477526fdbee1fe52fe1864bb6b7ab6d80 1b565047893eb8f55e839a9f0b5259d047547a82 $25.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/03/15 * 6c602e796082c3846297f7301fa0702f031a1647 f2580c2fa4873496427487e068658993bbf70894 $5.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/03/16 * b5a53b07e5ff84d945366dde1a100710e7bc2c2c 1b565047893eb8f55e839a9f0b5259d047547a82 $9.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/03/17 * f50bc0cf6300861a90dc5b29443cd614a09ffd88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $84.81 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/17 * 48227adff9f10cd94a570d7c9b7f6fe62cb6f4cb 1b565047893eb8f55e839a9f0b5259d047547a82 $6.45 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/17 * efccd2eb4dbdc2e2f6c7502eeab7816e686cbc8f f2580c2fa4873496427487e068658993bbf70894 $3.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/03/18 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 6.038000 EEEEE c56a21d23a6535184e7152ee138c28974f14280c $-180.00 2003/03/18 * fa39c23bd7d06b52f53d88060f6522ed961cc7e8 1b565047893eb8f55e839a9f0b5259d047547a82 $3.28 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/18 * fa39c23bd7d06b52f53d88060f6522ed961cc7e8 3282f21c97a0e1f66185923328d80d87fa5d8db7 $24.05 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/18 * fa73d67b17a393d9db8c96a9f3ec222804fe0fda 9c484b5dc87055f93751ad00947fd9a7a14ea470 $96.00 4907823cffe667ad9decdcdd3a4780c15485c6ea 2003/03/18 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 19.530000 AAAAA c56a21d23a6535184e7152ee138c28974f14280c $-360.01 2003/03/18 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 8.452000 GGGGG c56a21d23a6535184e7152ee138c28974f14280c $-180.02 2003/03/21 * 6c602e796082c3846297f7301fa0702f031a1647 f2580c2fa4873496427487e068658993bbf70894 $8.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/03/21 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $17.07 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/21 * ab77b70037475007e0bf5fb4a3bbb53e817f64dd 1b565047893eb8f55e839a9f0b5259d047547a82 $5.48 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/22 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.58 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-23.58 2003/03/23 * 856c722d0a5c00b903ab4d7c2f93505edb13bb93 1b565047893eb8f55e839a9f0b5259d047547a82 $2.42 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/23 * 405295e72fc5fb41cb97a68a217108b08b4e045e 1b565047893eb8f55e839a9f0b5259d047547a82 $3.28 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/24 * (104) cbf3dd2fe54446a3318e1632bc46a8ce5d491c07 2ff50cab09d039eaa717cac29cf2759eb39a007d $10,000.00 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 2003/03/24 * c5e1c75940ae9c61c63e88e2ea2ea0ddc267e80d 1b565047893eb8f55e839a9f0b5259d047547a82 $8.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/25 * 66686066ebe4e49d4e972927e6b74b60b35f31b1 f0d45665b22d0562833aa3bf373c5b15640d833e $32.28 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/03/26 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $7,745.85 677639d3e48c3ac2413f12c1a3e6b67525e09009 $548.42 4fd4e6978513bf18a906891e8e8c4b307ae3565e $247.79 a64166a90252d444071c62e9e0746ce6e83234b6 $57.95 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $98.72 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $16.15 c56a21d23a6535184e7152ee138c28974f14280c $600.02 (845ac5d9910830a5764c934bf791195b0fcd91f4) $114.00 39189083b8637c7fff89e6bcf808790861417796 $-4,000.15 15d4990b82d33262ff733b3e6539d66a0445c193 $-5,314.75 2003/03/26 * 91361769cb74ddd9f9fc545ca4f1bc4e4d7d7212 c56a21d23a6535184e7152ee138c28974f14280c $120.01 775a73f9b8633e396be7cfd03b6a46f07b47c5ef 2003/03/26 * 2b49ac1d006167806bc42ce9aec3e021b191e471 f0d45665b22d0562833aa3bf373c5b15640d833e $21.95 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/26 * 2a6badc29da609a5213520e707f23937f6868ea3 fc0e191163be4d1966e3c51b1635401f9e82a807 $50,000.00 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 2003/03/27 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $2.21 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/27 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 6.110000 EEEEE c56a21d23a6535184e7152ee138c28974f14280c $-180.00 2003/03/27 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 19.620000 AAAAA c56a21d23a6535184e7152ee138c28974f14280c $-360.01 2003/03/27 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 8.464000 GGGGG c56a21d23a6535184e7152ee138c28974f14280c $-180.02 2003/03/27 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad fc0e191163be4d1966e3c51b1635401f9e82a807 $0.28 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/03/27 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/27 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.98 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/27 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $9.46 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-29.46 2003/03/28 * 6f5760cc33ed56ddc9a6400b6af23a1a6ee2ccf2 f2580c2fa4873496427487e068658993bbf70894 $8.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/03/28 * 2d298800f1e4be0638155d9fe2170eba13557611 1b565047893eb8f55e839a9f0b5259d047547a82 $4.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/03/30 * a0348dae1e8e55995eb3140e80c411954008c1b0 1b565047893eb8f55e839a9f0b5259d047547a82 $7.27 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/30 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $170.64 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/03/30 * dc53165e7620d16c1e26d61168889930b913ec30 fc0e191163be4d1966e3c51b1635401f9e82a807 $1.76 e8a7204939bd995e0343c42de4366ad4fb95fe4d 2003/03/31 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $10.64 cfd76529eda7575c434ab6edd70e56693f979bb1 $10.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-20.64 2003/03/31 * db16aa488f49561098cd08c4749d94256c733b64 463628a20f371d71d46a7947f1175a0c16ce2f45 $450.00 11c48bb7aa6231a23d96299904885620d9fb3b1a 2003/03/31 * 32fcd74c867c3ddbff9b8d38e8294d6727b1f3a2 e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $20.00 88671479b5cf1fbd5df40139835b336f10e4c7a1 2003/03/31 * (2119) 19ebae82957ef58483f1354c5b179fcdf5394fcd 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $250.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/31 * (2118) d1704e602da55041cc9c5f83a1076b1551c1225a 11c48bb7aa6231a23d96299904885620d9fb3b1a $450.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/03/31 * 8f2a9369c196f88970181565a81aab1d8816b126 1b565047893eb8f55e839a9f0b5259d047547a82 $11.41 cfd76529eda7575c434ab6edd70e56693f979bb1 $21.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-32.41 2003/04/01 * cbb4cc49824bf79827cde838e005848027ca0a38 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $50,005.00 fc0e191163be4d1966e3c51b1635401f9e82a807 2003/04/01 * 893268b30e0a141b8640052be1e47d440cbd51d0 5ea6ff037c50e85215211c1c1a25eebf6014611e $49.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/01 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $36.62 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-56.62 2003/04/01 * fa39c23bd7d06b52f53d88060f6522ed961cc7e8 3282f21c97a0e1f66185923328d80d87fa5d8db7 $26.22 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/04/02 * 9683fba84d910707cd96a7afcc0ddbc0162ae030 1b565047893eb8f55e839a9f0b5259d047547a82 $9.66 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/02 * a4c1053a2870f13b1bfdd38db80b5d72acfba4d2 c233d176ce06c06ecfd032230c4be5ff4476a554 $19.60 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/04 * 8f2a9369c196f88970181565a81aab1d8816b126 1b565047893eb8f55e839a9f0b5259d047547a82 $18.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/04 * cc9de4beb764c10c43d0de61ae9a5235b049c860 1b565047893eb8f55e839a9f0b5259d047547a82 $10.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/05 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $14.38 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/05 * 299eb28835cafffe80dbb8398c478bd6b35b34bf 1b565047893eb8f55e839a9f0b5259d047547a82 $3.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/05 * 504c6c141d7ed2aa8a8a8786be2225c39cee7a0f 88671479b5cf1fbd5df40139835b336f10e4c7a1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/04/05 * 0f4b0afecf3d935e7bc810e1e0920cc8ee6f15ec 6d93163434f5b2253b3b5283f6015b60c79ea659 $3.43 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-23.43 2003/04/07 * be95ab7efd9f203a652c73b9031c74106f994112 181884ada86d2c3e7511e3ef3830fcf9f75b1421 $2.67 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/07 * 14a4b0336015e7b388359c2399c2d96845f3225e 181884ada86d2c3e7511e3ef3830fcf9f75b1421 $2.13 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/07 * 14a4b0336015e7b388359c2399c2d96845f3225e ac98afc9665076c4a5755414b1a6790587f3f61d $97.92 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/07 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $25.84 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-45.84 2003/04/07 * (2120) 8ca15484a6a50617febf823f3fa1c9e7038688ee 81234cef338d70f98ca31d7d2ea1e53e65a4f4a6 $5.34 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/04/08 * 4ab92ebbd07dc710dd4fd5bd35a51d063b8b192e dc11d67d6cc4ca6136c8690e387e70772dcb8465 $32.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/08 * af636338204eb20a819cb1da004e0332d82b995e 7134396063db3d3d81defdb1a2c68ee1383d199f $11.84 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/08 * 86de503686d1c3ce821d65d3013305099b1dba57 dc11d67d6cc4ca6136c8690e387e70772dcb8465 $19.37 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/08 * fa39c23bd7d06b52f53d88060f6522ed961cc7e8 3282f21c97a0e1f66185923328d80d87fa5d8db7 $26.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/08 * 606ec6e9bd8a8ff2ad14e5fade3f264471e82251 628072f9bfc87daeb5e0144ef2090ef8057935e8 $98.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/08 * (2121) bfe57c24b1164278d38739f81ca886131f8786aa 2ff50cab09d039eaa717cac29cf2759eb39a007d $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/04/09 * b5a53b07e5ff84d945366dde1a100710e7bc2c2c 1b565047893eb8f55e839a9f0b5259d047547a82 $13.00 f0d45665b22d0562833aa3bf373c5b15640d833e 2003/04/09 * 0bdfa162041573731f35ed9a115cebcceba07a68 592a60e960113a755f70f9b58ef7a97b5aca14dc $64.45 f0d45665b22d0562833aa3bf373c5b15640d833e $32.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-64.45 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $-32.22 2003/04/09 * 902328fdb92afdd6f4228b37cf9330a5560a7ec9 9c484b5dc87055f93751ad00947fd9a7a14ea470 $5.34 81234cef338d70f98ca31d7d2ea1e53e65a4f4a6 2003/04/10 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $2,432.06 677639d3e48c3ac2413f12c1a3e6b67525e09009 $548.42 4fd4e6978513bf18a906891e8e8c4b307ae3565e $247.01 a64166a90252d444071c62e9e0746ce6e83234b6 $57.77 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $98.72 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $16.15 c56a21d23a6535184e7152ee138c28974f14280c $600.02 (845ac5d9910830a5764c934bf791195b0fcd91f4) $114.00 39189083b8637c7fff89e6bcf808790861417796 $-4,000.15 2003/04/10 * 03642d4c8cfd39cbe8e1bbce3179a0ca53935e30 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $104.04 f0d45665b22d0562833aa3bf373c5b15640d833e 2003/04/10 * (2122) e1bc40eb9e5cdab590fbf9e1075d4e3e219ad073 c0226fafdf9e6711ac9121cf263e2d50791859cb 173.820 CCCCC (845ac5d9910830a5764c934bf791195b0fcd91f4) $570.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-3,000.00 2003/04/10 * 0fb419d273cdd0cfe9825e94f91c2058e99c1f7a 1b565047893eb8f55e839a9f0b5259d047547a82 $8.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/10 * 90b08ef8dda426f62ad84b8181275f0494c1d835 1b565047893eb8f55e839a9f0b5259d047547a82 $6.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/11 * 856c722d0a5c00b903ab4d7c2f93505edb13bb93 1b565047893eb8f55e839a9f0b5259d047547a82 $13.30 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/12 * b5cd9e578b070853a53006aabc7b4ac08f2a5634 1b565047893eb8f55e839a9f0b5259d047547a82 $8.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/13 * 4d565ed871c3d07f2878d73e18576ceb83f8700b 1b565047893eb8f55e839a9f0b5259d047547a82 $9.89 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/13 * 9861ce541c17b11f627e71c26bf350b33141f62b 1b565047893eb8f55e839a9f0b5259d047547a82 $5.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/14 * 8a3f780d3b3b0968084ca4b17c81945bbf1313a1 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $47.75 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2003/04/14 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $60.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-61.50 2003/04/14 * 7b17de2b0f498812aae7790797cd9cfd6744497d f2580c2fa4873496427487e068658993bbf70894 $8.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/14 * 95124b1c42080a5a8ca490558a5ecff9254976fc 1b565047893eb8f55e839a9f0b5259d047547a82 $21.95 cfd76529eda7575c434ab6edd70e56693f979bb1 $10.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-31.95 2003/04/14 * 4f36cda4f67b75a4ef1e867a0a51313233c04879 677639d3e48c3ac2413f12c1a3e6b67525e09009 $2,292.00 f0d45665b22d0562833aa3bf373c5b15640d833e $1,146.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-2,292.00 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $-1,146.00 2003/04/14 * 2a9649e01b659d9afcee414f35ac009fb43eed0c 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $111.00 f0d45665b22d0562833aa3bf373c5b15640d833e $55.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-111.00 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $-55.50 2003/04/14 * 91361769cb74ddd9f9fc545ca4f1bc4e4d7d7212 c56a21d23a6535184e7152ee138c28974f14280c $120.01 775a73f9b8633e396be7cfd03b6a46f07b47c5ef 2003/04/15 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 19.150000 AAAAA c56a21d23a6535184e7152ee138c28974f14280c $-360.01 2003/04/15 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 8.198000 GGGGG c56a21d23a6535184e7152ee138c28974f14280c $-180.02 2003/04/15 * 1d3c457cbe3e35739086346e0a4048653efa42e7 c56a21d23a6535184e7152ee138c28974f14280c 5.920000 EEEEE c56a21d23a6535184e7152ee138c28974f14280c $-180.00 2003/04/15 * 9fca7b8d5eb6cc9b0272edc33d05a50c77fa5589 f2580c2fa4873496427487e068658993bbf70894 $8.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/15 * e4811328450a9391ead85ddbcf2f25cda2e4ae2a 1b565047893eb8f55e839a9f0b5259d047547a82 $9.43 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/16 * f50bc0cf6300861a90dc5b29443cd614a09ffd88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $705.77 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/04/16 * 9ff6a9a7bab441b118a9019bf2d71b383d45e1c2 1b565047893eb8f55e839a9f0b5259d047547a82 $29.81 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/17 * a9b1ff0e867b716c67d5fde2a4debeae33680767 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/18 * d32081745b974201fd00fd7a51593397f65e90fb 1b565047893eb8f55e839a9f0b5259d047547a82 $43.30 a70b28cd2c82422e6d011d08a07354b598647e21 $25.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-68.30 2003/04/18 * c93854544737d531f50beb467d31d8d475d3689f 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $412.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/19 * 2581e49665bf2c87bd92626e8d57b5c36bc81824 1b565047893eb8f55e839a9f0b5259d047547a82 $17.10 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/19 * fa73d67b17a393d9db8c96a9f3ec222804fe0fda 9c484b5dc87055f93751ad00947fd9a7a14ea470 $54.49 4907823cffe667ad9decdcdd3a4780c15485c6ea 2003/04/20 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.63 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/20 * 69f2e19499724e3daa9e84f5c1a4142cfe98889c 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $394.09 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/20 * d2c1a43ad8690a29876c057c40b1ee57985ea0be 1b565047893eb8f55e839a9f0b5259d047547a82 $5.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/21 * 856c722d0a5c00b903ab4d7c2f93505edb13bb93 1b565047893eb8f55e839a9f0b5259d047547a82 $3.23 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/21 * 20bc87fd47b41f9abf6815df7496e90345f7ed37 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 $36.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/21 * 299eb28835cafffe80dbb8398c478bd6b35b34bf 1b565047893eb8f55e839a9f0b5259d047547a82 $2.65 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/21 * e31437bf8090bfa5241296d0eafdb3ff965dd7ad 9c484b5dc87055f93751ad00947fd9a7a14ea470 $156.09 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/22 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.82 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/22 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $17.26 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-37.26 2003/04/22 * 58c0f13307f8ae95dae0f4d9c56b7237bfef70d6 1b565047893eb8f55e839a9f0b5259d047547a82 $5.49 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/04/23 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $9.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/24 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $9.84 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/24 * 183f36d6f93a6c3a9776e73e8043fac90691688e 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $2.27 f0d45665b22d0562833aa3bf373c5b15640d833e 2003/04/25 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $3,545.27 677639d3e48c3ac2413f12c1a3e6b67525e09009 $756.84 4fd4e6978513bf18a906891e8e8c4b307ae3565e $297.97 a64166a90252d444071c62e9e0746ce6e83234b6 $69.69 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $136.23 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $9.69 39189083b8637c7fff89e6bcf808790861417796 $-4,815.69 2003/04/25 * (2123) 35b7ad4a355641a58d7ef29cce5ce8dadc550d35 cecae7f2312046d2775a401cc3c3925b79676ce3 $30.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/04/26 * 3e19ef28eacde8d6883cff95fa747530e063b41f 3282f21c97a0e1f66185923328d80d87fa5d8db7 $22.07 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/27 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $13.30 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/28 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $2.09 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/04/28 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $40.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-41.50 2003/04/28 * 91c59df073c8abd416047acbac4f38dd4834d955 1b565047893eb8f55e839a9f0b5259d047547a82 $9.94 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/28 * 0ab795183acd4eb0c332210e6923567f143d89c2 81e80d3e70928f8f87ca90648f81a63efe7625f5 $40.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/29 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df c233d176ce06c06ecfd032230c4be5ff4476a554 $10.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/29 * de358b30b4412964cb0ca6d32d8abff18ecbf961 c233d176ce06c06ecfd032230c4be5ff4476a554 $1.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/29 * de358b30b4412964cb0ca6d32d8abff18ecbf961 c233d176ce06c06ecfd032230c4be5ff4476a554 $9.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/29 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df 1b565047893eb8f55e839a9f0b5259d047547a82 $5.39 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/30 * dc53165e7620d16c1e26d61168889930b913ec30 fc0e191163be4d1966e3c51b1635401f9e82a807 $0.84 e8a7204939bd995e0343c42de4366ad4fb95fe4d 2003/04/30 * 51520e9e33ca70311b1deeaa3f3b8d85e5affea8 48bf83c24e711997303a05a83ee60500102f2976 $848.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/30 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $9.53 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/30 * c724d9c91df5cb3bd4441a2cd9e15676c61555f7 5ea6ff037c50e85215211c1c1a25eebf6014611e $81.92 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/04/30 * d0ce056a8a02b5d15225f39beb931bde9179fec5 1b565047893eb8f55e839a9f0b5259d047547a82 $5.60 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/30 * 97a7a570c8ede9be3bb8ca6edb1ee14e1012c595 1b565047893eb8f55e839a9f0b5259d047547a82 $5.80 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/04/30 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $162.00 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/05/01 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $5.16 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/01 * db16aa488f49561098cd08c4749d94256c733b64 463628a20f371d71d46a7947f1175a0c16ce2f45 $450.00 11c48bb7aa6231a23d96299904885620d9fb3b1a 2003/05/02 * 6c602e796082c3846297f7301fa0702f031a1647 f2580c2fa4873496427487e068658993bbf70894 $8.75 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/05/03 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df 1b565047893eb8f55e839a9f0b5259d047547a82 $6.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/03 * cc9de4beb764c10c43d0de61ae9a5235b049c860 1b565047893eb8f55e839a9f0b5259d047547a82 $6.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/05/05 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $4.96 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-24.96 2003/05/05 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.28 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/06 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $9.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/06 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.53 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/06 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $14.27 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/07 * 902328fdb92afdd6f4228b37cf9330a5560a7ec9 9c484b5dc87055f93751ad00947fd9a7a14ea470 $12.27 81234cef338d70f98ca31d7d2ea1e53e65a4f4a6 2003/05/07 * 39ccba9779f59020ae45a9df08864376a1011b80 1b565047893eb8f55e839a9f0b5259d047547a82 $10.78 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/08 * (2124) fa73d67b17a393d9db8c96a9f3ec222804fe0fda 4907823cffe667ad9decdcdd3a4780c15485c6ea $54.49 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/05/08 * (2125) 8ca15484a6a50617febf823f3fa1c9e7038688ee 81234cef338d70f98ca31d7d2ea1e53e65a4f4a6 $12.27 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/05/09 * 04ff4313e11dca88fb1707316329ff143f8f78c2 1b565047893eb8f55e839a9f0b5259d047547a82 $5.02 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/09 * 6311a5e415b6218c88c877cf57d459a79a646a22 1a8916d9798e34d59fd604a7ebde9d5054c84b70 $17.90 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/09 * 39ccba9779f59020ae45a9df08864376a1011b80 1b565047893eb8f55e839a9f0b5259d047547a82 $7.21 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/09 * 353483ad2a505c5f08dc1dc306993f394602c536 c233d176ce06c06ecfd032230c4be5ff4476a554 $14.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/09 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $19.18 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-39.18 2003/05/11 * f25484d2cb3be21faecbe8b4d73dfd1f71c81415 6d93163434f5b2253b3b5283f6015b60c79ea659 $4.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/13 * 75efa04022957aea8efebc48dd0bd95b636049c1 71c454369dde750a77c84ae295ddb94bfc43ec16 $8.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/05/14 * 30ae3121b82a0a4f50abfc80fc5ea66cfeb184ce f2580c2fa4873496427487e068658993bbf70894 $3.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/05/14 * 8f2a9369c196f88970181565a81aab1d8816b126 1b565047893eb8f55e839a9f0b5259d047547a82 $19.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/14 * 47bd01914364608da003849c18f7774a4b9c5309 1b565047893eb8f55e839a9f0b5259d047547a82 $2.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/14 * 299eb28835cafffe80dbb8398c478bd6b35b34bf 1b565047893eb8f55e839a9f0b5259d047547a82 $9.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/15 * 6f5760cc33ed56ddc9a6400b6af23a1a6ee2ccf2 f2580c2fa4873496427487e068658993bbf70894 $8.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/05/15 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $4.86 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-24.86 2003/05/15 * e31437bf8090bfa5241296d0eafdb3ff965dd7ad 9c484b5dc87055f93751ad00947fd9a7a14ea470 $37.09 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/16 * f50bc0cf6300861a90dc5b29443cd614a09ffd88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $633.26 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/05/16 * d0ce056a8a02b5d15225f39beb931bde9179fec5 1b565047893eb8f55e839a9f0b5259d047547a82 $4.20 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/16 * e313acc89483a750c6d21ef204fe7913fcb2db7c 5ea6ff037c50e85215211c1c1a25eebf6014611e $144.34 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/17 * 9861ce541c17b11f627e71c26bf350b33141f62b 1b565047893eb8f55e839a9f0b5259d047547a82 $2.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/17 * 0fb419d273cdd0cfe9825e94f91c2058e99c1f7a 1b565047893eb8f55e839a9f0b5259d047547a82 $7.70 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/17 * 47bd01914364608da003849c18f7774a4b9c5309 1b565047893eb8f55e839a9f0b5259d047547a82 $1.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/17 * (2126) bfe57c24b1164278d38739f81ca886131f8786aa 2ff50cab09d039eaa717cac29cf2759eb39a007d $25.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/05/18 * 6e1dffe991650653e71c21ac5c534f5bbb159f18 3282f21c97a0e1f66185923328d80d87fa5d8db7 $23.69 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/18 * 3188e18375b5076df0c9d2dffc7de069ff2bf422 1b565047893eb8f55e839a9f0b5259d047547a82 $6.37 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/19 * 47bd01914364608da003849c18f7774a4b9c5309 1b565047893eb8f55e839a9f0b5259d047547a82 $4.78 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/19 * (2127) 3561bec15d2b90f18a703a6503e4b02faa408fdd e940525b878f1783a30bf0cd9c040f6d27e0568c $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/05/20 * e31437bf8090bfa5241296d0eafdb3ff965dd7ad 9c484b5dc87055f93751ad00947fd9a7a14ea470 $7.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/20 * cf210ef5c8d4334c58a16819fdf1e249db004809 1b565047893eb8f55e839a9f0b5259d047547a82 $7.51 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/20 * cf210ef5c8d4334c58a16819fdf1e249db004809 1b565047893eb8f55e839a9f0b5259d047547a82 $5.11 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/20 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $17.61 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/20 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 1b565047893eb8f55e839a9f0b5259d047547a82 $2.59 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/21 * 56d273aa71c0343175835d0446e4227e8281b5e3 1b565047893eb8f55e839a9f0b5259d047547a82 $5.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/05/21 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/22 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-101.00 2003/05/22 * 6e1dffe991650653e71c21ac5c534f5bbb159f18 3282f21c97a0e1f66185923328d80d87fa5d8db7 $22.03 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/22 * b883e3aa0affdec26d5881142b07325aafadec89 92a772d9a491a8c8f239d9148b979f1da7369480 $3.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/23 * 710989ca32db257189a0fa0ce2cbd146542cd53b 1b565047893eb8f55e839a9f0b5259d047547a82 $6.79 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/23 * 4ffc4349510313ea639fc1e50d7e156c456cdb84 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/05/24 * 478b8cf87b83049bacb3b8d81693a1a9ee1a2af9 f2580c2fa4873496427487e068658993bbf70894 $5.76 cfd76529eda7575c434ab6edd70e56693f979bb1 $5.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-10.76 2003/05/24 * (2128) bea8d5e2678e516790a1d65f5c22875c1ee81f09 9c484b5dc87055f93751ad00947fd9a7a14ea470 $60.50 f2580c2fa4873496427487e068658993bbf70894 $9.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-70.00 2003/05/24 * 710989ca32db257189a0fa0ce2cbd146542cd53b 1b565047893eb8f55e839a9f0b5259d047547a82 $5.03 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/24 * cdbd6445b409d06d3bbeee9ad27c1d535ba7f6df 1b565047893eb8f55e839a9f0b5259d047547a82 $4.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/25 * be70ff73a1b22f302bc8af7bd32ee4273f2d8636 f2580c2fa4873496427487e068658993bbf70894 $6.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/05/25 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/25 * d2667e42a8beac84d6f95887ab807a947a425d67 1b565047893eb8f55e839a9f0b5259d047547a82 $5.66 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/26 * f9c0127a2f9298b71d7139c40fcdfd3a8ca7a670 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $25.00 a70b28cd2c82422e6d011d08a07354b598647e21 2003/05/26 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $1,545.11 677639d3e48c3ac2413f12c1a3e6b67525e09009 $192.35 4fd4e6978513bf18a906891e8e8c4b307ae3565e $118.97 a64166a90252d444071c62e9e0746ce6e83234b6 $27.82 37beb706a7535f3da1e5f5411c5c15bd4115a4bb $34.62 39189083b8637c7fff89e6bcf808790861417796 $-1,918.87 2003/05/26 * 710989ca32db257189a0fa0ce2cbd146542cd53b 1b565047893eb8f55e839a9f0b5259d047547a82 $2.98 cfd76529eda7575c434ab6edd70e56693f979bb1 $4.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-6.98 2003/05/26 * f1ba777d5cb86fe74056909a67c8d1ef5da7f8a9 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/05/29 * 1179ed4613d2431a8e74caafcc139d3a02926714 d7e87874854606f9910166abbf7e603606f0010c $21.34 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/29 * 11208e4d2c3ad4fdaab5633e552930c53992fac3 192c6a87c9e21761ef867c199811018469d948eb $53.35 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/05/30 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $178.45 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/05/31 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $170.50 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/05/31 * 041cf7433e58e494cb7f0c7b5e03e8171366e1b4 192c6a87c9e21761ef867c199811018469d948eb $847.71 b4b664a91f882e715c97f7eb9069a7d79797f07b $160.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-1,007.71 2003/06/02 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $179.97 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/03 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $178.84 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/04 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $178.24 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/05 * c0d1a562583cec8ebcfeea3d086f9d07047efce2 192c6a87c9e21761ef867c199811018469d948eb $27.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/06/07 * d250dcaaf8df1331e9e62e28c909f3ae9928b6d0 d7e87874854606f9910166abbf7e603606f0010c $92.89 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/06/09 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $201.63 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/11 * 9126b4d2ae43038749feb7efd0d929f923cb2ccc fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $89.17 904eafc4a3d3e7ee665de39f7baa66d9785b3c98 2003/06/12 * 041cf7433e58e494cb7f0c7b5e03e8171366e1b4 48bf83c24e711997303a05a83ee60500102f2976 $78.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/06/15 * e31437bf8090bfa5241296d0eafdb3ff965dd7ad 9c484b5dc87055f93751ad00947fd9a7a14ea470 $11.75 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/06/16 * 041cf7433e58e494cb7f0c7b5e03e8171366e1b4 48bf83c24e711997303a05a83ee60500102f2976 $44.36 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/06/16 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $2,345.10 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/16 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $179.73 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/19 * cbb4cc49824bf79827cde838e005848027ca0a38 c56a21d23a6535184e7152ee138c28974f14280c 331.296869 LMVTX @ $53.6599999999999999998612221219 c56a21d23a6535184e7152ee138c28974f14280c -523.942988 EEEEE @ $33.9299999999999999998438748872 c56a21d23a6535184e7152ee138c28974f14280c 55.981364 LMVTX @ $53.6599999999999999998612221219 c56a21d23a6535184e7152ee138c28974f14280c -88.534054 EEEEE @ $33.9299999999999999998438748872 2003/06/23 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $177.81 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/25 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $175.55 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/25 * f78a1b5ad54150ad91ac6f69b1836ab8a8c4f59c 48bf83c24e711997303a05a83ee60500102f2976 $13.07 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/06/26 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $58.23 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/27 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $186.58 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/30 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $172.81 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/30 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $172.81 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/06/30 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $165.27 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/07/01 * (103) cbb4cc49824bf79827cde838e005848027ca0a38 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $10,000.00 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 2003/07/03 * 58d902c2682a604912762fccf312f03da2581284 d7e87874854606f9910166abbf7e603606f0010c $40.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/07/07 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $174.78 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/07 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $174.19 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/10 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $171.67 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/10 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $171.67 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/11 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $172.17 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/14 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $171.76 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/14 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $172.58 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/15 * e31437bf8090bfa5241296d0eafdb3ff965dd7ad 9c484b5dc87055f93751ad00947fd9a7a14ea470 $11.75 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/07/16 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $1,616.30 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/16 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $171.27 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/21 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $203.14 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/21 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $170.19 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/21 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $171.11 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/23 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $171.97 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/24 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $172.10 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/28 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $174.15 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/28 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $174.92 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/29 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $208.78 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/30 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $174.36 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/07/31 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $158.25 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/08/04 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $202.43 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/04 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $202.43 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/06 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $205.56 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/06 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $172.35 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/08 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $205.78 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/11 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $205.53 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/11 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $171.82 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/13 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $205.16 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/14 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $170.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/15 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $109.66 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/08/17 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/17 * 344a788f45ac818f83648b6c76a1538282c1fcfc d7e87874854606f9910166abbf7e603606f0010c $4,224.52 192c6a87c9e21761ef867c199811018469d948eb $2,574.00 cfd76529eda7575c434ab6edd70e56693f979bb1 $-6,798.52 2003/08/17 * 5e5ac02782568145cf77bb0dbf15d2b37174f54f 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/08/18 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $200.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-201.50 2003/08/18 * a68640eec7fa0e2090324c1f649ae961549fe98e 1b565047893eb8f55e839a9f0b5259d047547a82 $12.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/18 * e31437bf8090bfa5241296d0eafdb3ff965dd7ad 9c484b5dc87055f93751ad00947fd9a7a14ea470 $11.76 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/19 * 3837f7cdd2718dc7c9adb5697841d95b3b48e412 1b565047893eb8f55e839a9f0b5259d047547a82 $6.87 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/08/19 * 710989ca32db257189a0fa0ce2cbd146542cd53b 1b565047893eb8f55e839a9f0b5259d047547a82 $15.65 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/20 * 710989ca32db257189a0fa0ce2cbd146542cd53b 1b565047893eb8f55e839a9f0b5259d047547a82 $22.90 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/20 * c6f1f561f070c16cc7a1d989b24fd466e041f909 f2580c2fa4873496427487e068658993bbf70894 $10.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/08/22 * 1d4fdf992a2f21724715605e9157c282de68924d 1b565047893eb8f55e839a9f0b5259d047547a82 $16.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/22 * 710989ca32db257189a0fa0ce2cbd146542cd53b 1b565047893eb8f55e839a9f0b5259d047547a82 $5.88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/23 * 91c59df073c8abd416047acbac4f38dd4834d955 1b565047893eb8f55e839a9f0b5259d047547a82 $6.56 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/08/23 * 353483ad2a505c5f08dc1dc306993f394602c536 c233d176ce06c06ecfd032230c4be5ff4476a554 $23.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/23 * 61ad50a9b9189cc3cf1874568e35e7901ff4c982 c233d176ce06c06ecfd032230c4be5ff4476a554 $48.51 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/24 * 6311a5e415b6218c88c877cf57d459a79a646a22 1b565047893eb8f55e839a9f0b5259d047547a82 $3.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/25 * 0fb419d273cdd0cfe9825e94f91c2058e99c1f7a 1b565047893eb8f55e839a9f0b5259d047547a82 $11.64 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/28 * 2470dca59588f4db61caa65758dc3be6e848d811 1b565047893eb8f55e839a9f0b5259d047547a82 $5.70 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/29 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.67 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/29 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $31.56 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/29 * 09c0b607d75b45cfe399ef37683597e6010ad3b4 1b565047893eb8f55e839a9f0b5259d047547a82 $7.65 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/30 * 0664566aef3c0cf8fe661bc4bb62e31e4e8fd435 c233d176ce06c06ecfd032230c4be5ff4476a554 $9.80 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/08/31 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $21.69 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/31 * 39ccba9779f59020ae45a9df08864376a1011b80 1b565047893eb8f55e839a9f0b5259d047547a82 $7.01 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/08/31 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $119.98 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/09/01 * 63d0a265712c9c53d03cd1bde96c16ea9b114b06 1b565047893eb8f55e839a9f0b5259d047547a82 $12.86 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/02 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $13.35 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/02 * 7994a463ade0f972bf1bb59ac236e37b08bfc4d7 628072f9bfc87daeb5e0144ef2090ef8057935e8 $202.39 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/09/03 * cdbd6445b409d06d3bbeee9ad27c1d535ba7f6df 6d93163434f5b2253b3b5283f6015b60c79ea659 $3.21 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/03 * 856c722d0a5c00b903ab4d7c2f93505edb13bb93 1b565047893eb8f55e839a9f0b5259d047547a82 $14.36 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/04 * 8719b3bde8326bcc3e3dcc12c2ffb7fea691b1cc 1b565047893eb8f55e839a9f0b5259d047547a82 $10.29 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/05 * 15a05d714d00be0c9802f0ec604d52966a3ceb10 1b565047893eb8f55e839a9f0b5259d047547a82 $8.91 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/05 * 4c1b9c1def0a6e56d3d22891a22c7e171d9133f0 1b565047893eb8f55e839a9f0b5259d047547a82 $9.90 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/06 * 04ff4313e11dca88fb1707316329ff143f8f78c2 1b565047893eb8f55e839a9f0b5259d047547a82 $10.03 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/06 * 478b8cf87b83049bacb3b8d81693a1a9ee1a2af9 f2580c2fa4873496427487e068658993bbf70894 $10.33 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/06 * 39ccba9779f59020ae45a9df08864376a1011b80 1b565047893eb8f55e839a9f0b5259d047547a82 $7.45 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/07 * 685f23bc5eb93fc982292395e8c03a40670ce3ec 1b565047893eb8f55e839a9f0b5259d047547a82 $3.82 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/07 * 9861ce541c17b11f627e71c26bf350b33141f62b 1b565047893eb8f55e839a9f0b5259d047547a82 $1.89 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/09/07 * a06a7623ea4babf483fa88f403655cc42be3fe14 1b565047893eb8f55e839a9f0b5259d047547a82 $11.45 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/08 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $200.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/09/08 * 2470dca59588f4db61caa65758dc3be6e848d811 1b565047893eb8f55e839a9f0b5259d047547a82 $5.59 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/08 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $16.96 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/08 * f1a1dcb303695a9107bef9686ec91b4fb3f2e19f 10cab4f7105feed78ca34f97ba79d013cb8e28f4 $15.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/08 * f1a1dcb303695a9107bef9686ec91b4fb3f2e19f 10cab4f7105feed78ca34f97ba79d013cb8e28f4 $15.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/08 * 6f5760cc33ed56ddc9a6400b6af23a1a6ee2ccf2 f2580c2fa4873496427487e068658993bbf70894 $17.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/09/09 * 39ccba9779f59020ae45a9df08864376a1011b80 1b565047893eb8f55e839a9f0b5259d047547a82 $7.34 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/09 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/09 * 9861ce541c17b11f627e71c26bf350b33141f62b 1b565047893eb8f55e839a9f0b5259d047547a82 $2.79 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/10 * 478b8cf87b83049bacb3b8d81693a1a9ee1a2af9 f2580c2fa4873496427487e068658993bbf70894 $4.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/10 * f1a1dcb303695a9107bef9686ec91b4fb3f2e19f 10cab4f7105feed78ca34f97ba79d013cb8e28f4 $10.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/11 * cdbd6445b409d06d3bbeee9ad27c1d535ba7f6df 1b565047893eb8f55e839a9f0b5259d047547a82 $4.45 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/11 * f750981f288338dc2e2da75e826a63f54440d929 1b565047893eb8f55e839a9f0b5259d047547a82 $14.80 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/12 * 427dc6551f1155d59fceaabaeb34eafc400109b6 f2580c2fa4873496427487e068658993bbf70894 $7.75 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/09/12 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $28.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/13 * 6f5760cc33ed56ddc9a6400b6af23a1a6ee2ccf2 f2580c2fa4873496427487e068658993bbf70894 $5.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/09/13 * 48227adff9f10cd94a570d7c9b7f6fe62cb6f4cb 1b565047893eb8f55e839a9f0b5259d047547a82 $6.45 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/15 * 856c722d0a5c00b903ab4d7c2f93505edb13bb93 1b565047893eb8f55e839a9f0b5259d047547a82 $7.73 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/16 * 3e19ef28eacde8d6883cff95fa747530e063b41f 3282f21c97a0e1f66185923328d80d87fa5d8db7 $17.01 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/16 * 3e19ef28eacde8d6883cff95fa747530e063b41f 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.90 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/16 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $11.75 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/09/17 * 7c32c47523acc8ef0405a7554be36e6ddc02cb21 c233d176ce06c06ecfd032230c4be5ff4476a554 $9.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/18 * 3a8df97eb4b7f9359d704a4eff1adec76a44f5e7 1b565047893eb8f55e839a9f0b5259d047547a82 $9.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/18 * d66f20839763a4d86071659cdcbebaa6020203c3 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.35 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/09/18 * 3a8df97eb4b7f9359d704a4eff1adec76a44f5e7 3282f21c97a0e1f66185923328d80d87fa5d8db7 $23.29 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/19 * eda08d7efcc9dc0b10d261c85f3e2861819a60ce 1b565047893eb8f55e839a9f0b5259d047547a82 $19.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/19 * dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $31.54 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/20 * f159c0ab1d78ac3ddcd8264eed66b66516c7dd09 1b565047893eb8f55e839a9f0b5259d047547a82 $5.86 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/20 * 67892daef69b2043c22227601adcbfb25054ea39 1b565047893eb8f55e839a9f0b5259d047547a82 $10.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/21 * a1707a49d3dbd76e84fa94676f68c659af8133d1 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $246.75 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/21 * d2667e42a8beac84d6f95887ab807a947a425d67 1b565047893eb8f55e839a9f0b5259d047547a82 $12.73 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/22 * d08303b27dd02c246f06c8cf3969e3a04d54ea94 1b565047893eb8f55e839a9f0b5259d047547a82 $24.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/22 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.30 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/22 * 3e19ef28eacde8d6883cff95fa747530e063b41f 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.73 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/24 * 920f974dd8e6bbf94ef863cfbba66e67c347dc65 f2580c2fa4873496427487e068658993bbf70894 $10.00 1b565047893eb8f55e839a9f0b5259d047547a82 $15.00 cfd76529eda7575c434ab6edd70e56693f979bb1 $-25.00 2003/09/24 * 91c59df073c8abd416047acbac4f38dd4834d955 1b565047893eb8f55e839a9f0b5259d047547a82 $5.29 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/09/24 * ebd0c5f989f35ee5e746cc1ffeb823edee7846c3 1b565047893eb8f55e839a9f0b5259d047547a82 $25.18 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/24 * 95298d92d7000f1debf0ad06e7bd74c2af3823f4 1b565047893eb8f55e839a9f0b5259d047547a82 $3.39 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/25 * (2129) 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $30.00 b52415543ea21652cd42ab1cbd832ffe3ac46c18 $940.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-1,920.00 2003/09/25 * (2130) 19f3c81bfcf220f767c442ed5722e3191d604a98 463628a20f371d71d46a7947f1175a0c16ce2f45 $200.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-140.00 cfd76529eda7575c434ab6edd70e56693f979bb1 $-60.00 2003/09/26 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $15.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/26 * be95ab7efd9f203a652c73b9031c74106f994112 181884ada86d2c3e7511e3ef3830fcf9f75b1421 $7.40 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/09/26 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 6d93163434f5b2253b3b5283f6015b60c79ea659 $11.78 1b565047893eb8f55e839a9f0b5259d047547a82 $20.20 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-31.98 2003/09/26 * 50ee198a36346bb0c8bba88eb7636ce18bd4faaa 92a772d9a491a8c8f239d9148b979f1da7369480 $21.70 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/26 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-101.50 2003/09/26 * fafc3ee2608e739320f4cbe2db38fb45c30f64d0 1b565047893eb8f55e839a9f0b5259d047547a82 $10.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/26 * 61ad50a9b9189cc3cf1874568e35e7901ff4c982 7134396063db3d3d81defdb1a2c68ee1383d199f $5.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/26 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $14.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/26 * fafc3ee2608e739320f4cbe2db38fb45c30f64d0 7134396063db3d3d81defdb1a2c68ee1383d199f $59.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/27 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $5.88 6d93163434f5b2253b3b5283f6015b60c79ea659 $10.67 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-16.55 2003/09/27 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 6d93163434f5b2253b3b5283f6015b60c79ea659 $4.28 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/27 * 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $9.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/09/28 * a946632db5acafcf73e541098cacb660c6dda9e0 1b565047893eb8f55e839a9f0b5259d047547a82 $16.65 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/09/28 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 71c454369dde750a77c84ae295ddb94bfc43ec16 $8.99 c233d176ce06c06ecfd032230c4be5ff4476a554 $6.66 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-15.65 2003/09/29 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $7.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/09/30 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $111.38 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/09/30 * 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $7.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/09/30 * f7dfe4fbd5e0b9027298ac1a5543cc0f5c2a2a30 f1cd21cd287244422551d04963bc2cf4a5f30cde $7.03 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/09/30 * f7dfe4fbd5e0b9027298ac1a5543cc0f5c2a2a30 f1cd21cd287244422551d04963bc2cf4a5f30cde $129.88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/01 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $6.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/02 * a52b087bf5b2c8dba6d050f3cb79d42ce89826ff 1b565047893eb8f55e839a9f0b5259d047547a82 $31.46 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/02 a52b087bf5b2c8dba6d050f3cb79d42ce89826ff 1b565047893eb8f55e839a9f0b5259d047547a82 $12.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/03 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $5.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/03 * 06e1515d7acf1e9b3869e82b570fd55c60ed97c2 5ea6ff037c50e85215211c1c1a25eebf6014611e $27.04 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/03 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-101.50 2003/10/03 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $7.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/04 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.09 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/04 51055eddd536eaba10df20495173acfab1c6f52e 1b565047893eb8f55e839a9f0b5259d047547a82 $8.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/04 * 1133a4e496a435e39ee41202eb07f46637ef5b62 6d93163434f5b2253b3b5283f6015b60c79ea659 $17.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/04 * dfcde5b76512cd71d51002f76cc62cf9e25f0d77 1b565047893eb8f55e839a9f0b5259d047547a82 $19.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/04 * befeb39a3e027511ff64ff1d3b7232c6fe2da108 92a772d9a491a8c8f239d9148b979f1da7369480 $26.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/04 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $7.99 1b565047893eb8f55e839a9f0b5259d047547a82 $0.66 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-8.65 2003/10/04 * b45923836563f437a6394be8f4fd035bf7145f8d eb0c1e3629fe7cba500081ef756a72e9659a93c4 $4,289.30 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/06 * d39973357f14216e757ef5839d4fc571e1a69abf 1b565047893eb8f55e839a9f0b5259d047547a82 $6.87 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/06 * abec79e51147db01996bcff0e1eb2fef39c1bb68 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $198.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/06 * befeb39a3e027511ff64ff1d3b7232c6fe2da108 a7cefcc26daa42c746631597e921d13810dd1e5d $57.19 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/07 4dab783e187b6ba8917a01e93ceb1580321395f9 3e2706db92ca6bb952333fd028e582695910c01d $6.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/07 * 535e770a4622fe8e4aa42e54c99c859adc8e9f36 1b565047893eb8f55e839a9f0b5259d047547a82 $19.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/08 * 4331e9979566987b4e3c8e7868f918be5705df38 a7127d3fcaafa2eeaa5369ae245a4835a250d084 $39.95 1237fd153ab3077a51de74b5e659441b7bf6ef01 $8.05 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-48.00 2003/10/08 b5691b21f1b8e3bca1caee7269c873d9a93eb8e8 1b565047893eb8f55e839a9f0b5259d047547a82 $10.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/08 47d722f2ba03396fb8bbabf01fdbfc1cbfa6e7fe c233d176ce06c06ecfd032230c4be5ff4476a554 $20.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/14 * 2215b86d97712591977ef1bbffb35eb398e76624 1b565047893eb8f55e839a9f0b5259d047547a82 $56.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/14 c657a077d616bc8d5a7a82b2b1bf41292b4fe5b1 3e2706db92ca6bb952333fd028e582695910c01d $17.25 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/15 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.03 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/15 * 96a3c221e59f05da813698be1262f09f791aaa42 1b565047893eb8f55e839a9f0b5259d047547a82 $6.42 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/15 * 96a3c221e59f05da813698be1262f09f791aaa42 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $443.79 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/15 * c406fac5adce4abf7cd534369aa1ae3bd5360635 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $43.30 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/16 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $571.91 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/10/16 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $4.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/16 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $15.45 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/16 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.79 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-53.79 2003/10/16 * 6cfe6d803407aca6e30527c8a26fc31f5a9f7944 a7cefcc26daa42c746631597e921d13810dd1e5d $79.20 a7cefcc26daa42c746631597e921d13810dd1e5d $118.80 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-198.00 2003/10/17 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $10.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/17 e59a2ab6add54c1cf14f7764f53e4a0fa82cd542 f2580c2fa4873496427487e068658993bbf70894 $9.75 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/18 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/18 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $11.97 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/19 * b422694a3e6330fa33f2996acb27cc83093c1cdf dc11d67d6cc4ca6136c8690e387e70772dcb8465 $19.43 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/19 * a7791c30537bb428e31775f1699cf2258acd4921 dc11d67d6cc4ca6136c8690e387e70772dcb8465 $53.46 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/19 bdd0da1d8175aae2d2b23628e037210e17dfa259 1b565047893eb8f55e839a9f0b5259d047547a82 $18.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/20 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $40.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/10/20 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/20 9fca7b8d5eb6cc9b0272edc33d05a50c77fa5589 f2580c2fa4873496427487e068658993bbf70894 $5.75 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/20 * a52b087bf5b2c8dba6d050f3cb79d42ce89826ff 1b565047893eb8f55e839a9f0b5259d047547a82 $5.00 cfd76529eda7575c434ab6edd70e56693f979bb1 $-5.00 1b565047893eb8f55e839a9f0b5259d047547a82 $7.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/21 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $6.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/21 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $8.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/22 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $5.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/22 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $1.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/22 * 3e19ef28eacde8d6883cff95fa747530e063b41f 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.53 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/22 * fafc3ee2608e739320f4cbe2db38fb45c30f64d0 1b565047893eb8f55e839a9f0b5259d047547a82 $10.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/22 * f7dfe4fbd5e0b9027298ac1a5543cc0f5c2a2a30 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $173.18 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/22 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $6.38 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/23 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $10.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/23 * eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $4.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/24 * b45923836563f437a6394be8f4fd035bf7145f8d eb0c1e3629fe7cba500081ef756a72e9659a93c4 $139.64 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/24 * 45c2dbcd2131ea9530c2615d971be996790da2b4 1b565047893eb8f55e839a9f0b5259d047547a82 $4.30 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/25 * c7fa9b02553297834a21dc022d55dede1c98580c 1b565047893eb8f55e839a9f0b5259d047547a82 $15.73 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/26 * 619cbfd21d72639f985324a9306696ce1262f3f6 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $7,000.00 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 2003/10/26 * (2131) 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $3.17 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/10/26 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $80.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-81.50 2003/10/27 * (2132) 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/10/27 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $9.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/27 * 024c1ec682f0ba522659c8afbbb4948b8d212f8f 1b565047893eb8f55e839a9f0b5259d047547a82 $4.64 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/27 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $2.97 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/28 ab77b70037475007e0bf5fb4a3bbb53e817f64dd 1b565047893eb8f55e839a9f0b5259d047547a82 $6.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/10/29 * 96a3c221e59f05da813698be1262f09f791aaa42 a7cefcc26daa42c746631597e921d13810dd1e5d $21.64 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/29 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $14.68 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/29 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $5.04 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/29 * 953388f315fdab9d0166aaa871ca84fc212af6fd 1b565047893eb8f55e839a9f0b5259d047547a82 $8.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/30 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $7.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/30 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $4.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/30 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $4.88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/31 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $112.00 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/10/31 * 6ed2ba8998c24c45c47b4a32078684e29ad113a4 59284ef86feceb946c427aeb6c0badfeb415b446 $10.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/10/31 * 6311a5e415b6218c88c877cf57d459a79a646a22 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $162.36 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/10/31 91c59df073c8abd416047acbac4f38dd4834d955 1b565047893eb8f55e839a9f0b5259d047547a82 $8.21 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/11/01 * 3e19ef28eacde8d6883cff95fa747530e063b41f 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.07 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/01 * 96e51d54e372ee3a69132305ed33b271d5414038 1b565047893eb8f55e839a9f0b5259d047547a82 $23.43 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/03 * a52b087bf5b2c8dba6d050f3cb79d42ce89826ff 1b565047893eb8f55e839a9f0b5259d047547a82 $37.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/03 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $9.81 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/04 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.14 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/04 * a52b087bf5b2c8dba6d050f3cb79d42ce89826ff 1b565047893eb8f55e839a9f0b5259d047547a82 $35.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/05 * b3dee3f782518ed1c60469f04f4fdca82e619534 f2580c2fa4873496427487e068658993bbf70894 $11.00 cfd76529eda7575c434ab6edd70e56693f979bb1 $10.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-21.00 2003/11/06 * (2133) 77d8308bb137226ac832a1e96ff81a6ae5a5d086 59284ef86feceb946c427aeb6c0badfeb415b446 $5.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/11/06 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $7.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/11/06 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $21.71 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-71.71 2003/11/07 * (2134) 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $21.09 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/11/07 * (2135) a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $65.41 e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $51.32 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-116.73 2003/11/07 * ce6771d870502fe52ab8ae77d8c7cab02879fde6 1b565047893eb8f55e839a9f0b5259d047547a82 $30.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/08 bce1b4b2f7f58595ffbcefe4614aafe1269c3000 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/11/09 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $14.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/11 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $5.89 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/12 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.19 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-53.19 2003/11/13 * 7994a463ade0f972bf1bb59ac236e37b08bfc4d7 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $11.14 e8a7204939bd995e0343c42de4366ad4fb95fe4d 2003/11/13 * (2136) 4a8d2c1469dba4cbe5e7cd2a99bc287765ea105a eb0c1e3629fe7cba500081ef756a72e9659a93c4 $500.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/11/13 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $7.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/11/13 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $20.83 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/14 * fd2d6dc6bf7e4124d5ddfa6a44275aa6d2f15ce8 1b565047893eb8f55e839a9f0b5259d047547a82 $8.14 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/15 a52b087bf5b2c8dba6d050f3cb79d42ce89826ff 1b565047893eb8f55e839a9f0b5259d047547a82 $16.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/11/16 * d66f20839763a4d86071659cdcbebaa6020203c3 3282f21c97a0e1f66185923328d80d87fa5d8db7 $21.97 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/11/16 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $17.24 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/16 * b3dee3f782518ed1c60469f04f4fdca82e619534 f2580c2fa4873496427487e068658993bbf70894 $16.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/11/17 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $6,061.95 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/11/17 * a91ab933c971ab9782b57b39377fc6b9aa878294 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $39.30 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/17 * 953388f315fdab9d0166aaa871ca84fc212af6fd 1b565047893eb8f55e839a9f0b5259d047547a82 $7.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/17 * eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $7.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/17 * 4886766974cb547d81289d92e2f87646a5715dcb 59284ef86feceb946c427aeb6c0badfeb415b446 $19.90 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/18 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $7.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/11/18 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $60.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-61.50 2003/11/19 5d143d62e98809ac444b228114a6cfde901bad91 1b565047893eb8f55e839a9f0b5259d047547a82 $5.08 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/11/19 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $11.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/19 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $5.99 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/11/20 * 97c39ef131bd74fb79b747076e3d51dfcd06a290 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $177.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/20 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $21.35 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/22 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $21.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/22 * 771469322c94ab3cb0b03e21018f3aa2b81abb11 1b565047893eb8f55e839a9f0b5259d047547a82 $9.29 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/22 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $24.54 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/23 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $9.52 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/11/23 * 95298d92d7000f1debf0ad06e7bd74c2af3823f4 c233d176ce06c06ecfd032230c4be5ff4476a554 $10.31 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/23 * 478b8cf87b83049bacb3b8d81693a1a9ee1a2af9 f2580c2fa4873496427487e068658993bbf70894 $12.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/26 * dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $146.30 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/28 * (2137) 923512d3f40f60de95daca10ec86a9810b1450ad 3e2706db92ca6bb952333fd028e582695910c01d $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/11/29 * 1ea8657593769ea85cca3652ce0132a1905e6ddb 1b565047893eb8f55e839a9f0b5259d047547a82 $21.34 cfd76529eda7575c434ab6edd70e56693f979bb1 $47.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-68.34 2003/11/29 * 987f1f9e420a473ad352b7ed9edf01e112842177 1b565047893eb8f55e839a9f0b5259d047547a82 $5.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/29 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/11/30 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $100.78 5c40e29310f67c1d38cd0a6251819dc2860aab37 2003/11/30 1e6df810ce83206bb4c563047d86d2a0b9765e65 1b565047893eb8f55e839a9f0b5259d047547a82 $11.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/11/30 c6f1f561f070c16cc7a1d989b24fd466e041f909 f2580c2fa4873496427487e068658993bbf70894 $7.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/11/30 * (2138) 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/12/01 * 846d6ebec66b4665b1e5ad1bad06a24dec170ab6 3282f21c97a0e1f66185923328d80d87fa5d8db7 $21.83 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/01 * 6e8301179a372b2284a1be31f62d401c2781b431 5ea6ff037c50e85215211c1c1a25eebf6014611e $59.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/02 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $7.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/12/02 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $6.29 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/02 * eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $7.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/02 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $14.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/03 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $4.73 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/03 99f35cfe037ce7200aab33a50ae0151c73244548 1b565047893eb8f55e839a9f0b5259d047547a82 $6.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2003/12/03 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $7.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/03 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $25.54 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/04 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $14.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/05 * f16cc2af0679eeb768ff057766a607279f6dea24 c233d176ce06c06ecfd032230c4be5ff4476a554 $8.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/06 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $23.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/07 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $12.28 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/08 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $5.84 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/08 * be95ab7efd9f203a652c73b9031c74106f994112 181884ada86d2c3e7511e3ef3830fcf9f75b1421 $8.26 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/08 * bfc69ecff2f2a51ff65958bf2107a1d38daff373 1b565047893eb8f55e839a9f0b5259d047547a82 $29.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/08 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $7.08 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-57.08 2003/12/09 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $12.01 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/10 * 5d143d62e98809ac444b228114a6cfde901bad91 1b565047893eb8f55e839a9f0b5259d047547a82 $4.69 1b565047893eb8f55e839a9f0b5259d047547a82 $0.39 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-5.08 2003/12/10 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $9.67 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/11 * f16cc2af0679eeb768ff057766a607279f6dea24 c233d176ce06c06ecfd032230c4be5ff4476a554 $7.56 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/12 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $21.35 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/12 * eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $4.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/13 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.43 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/14 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $11.28 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/14 * eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $4.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/17 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $17.45 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-67.45 2003/12/17 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/18 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $1,318.06 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/12/18 * 3ce79f1391f8596d7bead1a8ac4ef7f2b6948448 c233d176ce06c06ecfd032230c4be5ff4476a554 $4.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/19 * eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $4.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/19 * aba52d868db8a8abdbf5b6870eb786e08cbfae0c 1b565047893eb8f55e839a9f0b5259d047547a82 $22.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/19 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/20 * 22250d4f37cd5ca114deacbcce49798e78657179 1b565047893eb8f55e839a9f0b5259d047547a82 $15.89 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/20 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $22.59 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/20 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $22.82 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/21 * 6e011e2dd9b3f7b54c048e6b873c6242d6aaad3d 1b565047893eb8f55e839a9f0b5259d047547a82 $11.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/22 * (2139) 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $33.53 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/12/22 * (2140) 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2003/12/22 * 6e1dffe991650653e71c21ac5c534f5bbb159f18 1b565047893eb8f55e839a9f0b5259d047547a82 $5.32 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/22 * da1821d1c660f3714496c84298561544a9d98bba f2580c2fa4873496427487e068658993bbf70894 $16.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/23 * c6310ab22ed48f359022718d1b76e7876e9bb78b 1b565047893eb8f55e839a9f0b5259d047547a82 $27.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/23 * 353483ad2a505c5f08dc1dc306993f394602c536 c233d176ce06c06ecfd032230c4be5ff4476a554 $15.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/24 * 28ddee7891023e98fd5042ad8c0ed020d8658814 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.07 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/25 * 41f819b5353175ee634c3fc4ea22fcedee66762c 1b565047893eb8f55e839a9f0b5259d047547a82 $6.12 cecae7f2312046d2775a401cc3c3925b79676ce3 $0.75 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-6.87 2003/12/25 * 59e8bd32b95ddfd857b28cda46cd73ec96d1876e 1b565047893eb8f55e839a9f0b5259d047547a82 $24.72 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/25 * 3837f7cdd2718dc7c9adb5697841d95b3b48e412 1b565047893eb8f55e839a9f0b5259d047547a82 $6.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/25 * 06f745c5b09afa8c983759d4f364191d7296a779 f2580c2fa4873496427487e068658993bbf70894 $8.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/26 * 42312536c687162574c9e3fc559f66328b16dc0a 1b565047893eb8f55e839a9f0b5259d047547a82 $8.10 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/26 * 95b6b46bf9f262cf7aeffa04ba12d3bde9411eb6 1b565047893eb8f55e839a9f0b5259d047547a82 $27.24 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/27 * b6d03fa2a031c97515cf8e68ab32b80b8df4b658 1b565047893eb8f55e839a9f0b5259d047547a82 $15.00 cfd76529eda7575c434ab6edd70e56693f979bb1 $21.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-36.00 2003/12/27 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.19 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/27 * b441f825276ddad8aac9f1d66da347350b2df1d0 1b565047893eb8f55e839a9f0b5259d047547a82 $7.03 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/28 * 624de2e9a0226301c5a73ed4a6cb48dc23ca03a7 1b565047893eb8f55e839a9f0b5259d047547a82 $20.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/29 * 04ff4313e11dca88fb1707316329ff143f8f78c2 1b565047893eb8f55e839a9f0b5259d047547a82 $5.02 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/29 * 299eb28835cafffe80dbb8398c478bd6b35b34bf 1b565047893eb8f55e839a9f0b5259d047547a82 $4.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/29 * c50ffe9fd9ab159d36900b2a90d0db4ddf70311e 92a772d9a491a8c8f239d9148b979f1da7369480 $15.06 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/29 * 7994a463ade0f972bf1bb59ac236e37b08bfc4d7 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $42.17 e8a7204939bd995e0343c42de4366ad4fb95fe4d 2003/12/30 * 753f969617e5ddb15fff170a850849553001fa92 1b565047893eb8f55e839a9f0b5259d047547a82 $13.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/31 * fa39c23bd7d06b52f53d88060f6522ed961cc7e8 3282f21c97a0e1f66185923328d80d87fa5d8db7 $21.61 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/31 * 22b512a738ea5443add7cda4d716a447fc5e0a91 1b565047893eb8f55e839a9f0b5259d047547a82 $9.97 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2003/12/31 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $103.70 5c40e29310f67c1d38cd0a6251819dc2860aab37 2004/01/03 * af636338204eb20a819cb1da004e0332d82b995e dc11d67d6cc4ca6136c8690e387e70772dcb8465 $8.61 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/03 * c87c800004660d7f8be464b70bddd89e660faf91 e940525b878f1783a30bf0cd9c040f6d27e0568c $9.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/04 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $2.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-102.00 2004/01/04 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $22.94 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/05 * a8bca5fc944b8418636d585d965f0a4a5d172b2d 5ea6ff037c50e85215211c1c1a25eebf6014611e $22.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/05 * 0d3afc5ee7dca0e4384f36c77ddee97a299f4799 5ea6ff037c50e85215211c1c1a25eebf6014611e $12.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/05 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $11.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/06 * 04ff4313e11dca88fb1707316329ff143f8f78c2 1b565047893eb8f55e839a9f0b5259d047547a82 $5.02 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/06 * 38b7f9196b261fb4c1d940aedf1f9c1428392e54 289db8ff9e0e3209f73fdeecb035f47c88396ff4 $508.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/06 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $34.37 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/07 * 753f969617e5ddb15fff170a850849553001fa92 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $35.88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/07 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $20.54 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/07 * f8d333e34c16de2be20b5dc8fe8e37c8cf0add70 3282f21c97a0e1f66185923328d80d87fa5d8db7 $18.41 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/08 * 6e011e2dd9b3f7b54c048e6b873c6242d6aaad3d 1b565047893eb8f55e839a9f0b5259d047547a82 $11.69 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/08 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $28.11 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/10 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $11.43 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/11 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $18.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/11 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $13.01 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/11 * eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $81.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/12 * cbb4cc49824bf79827cde838e005848027ca0a38 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $5,000.00 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 2004/01/12 * 64b9f0a834be7951665cf032245a6a7c8c048d85 f2580c2fa4873496427487e068658993bbf70894 $13.99 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/01/14 * 5d143d62e98809ac444b228114a6cfde901bad91 1b565047893eb8f55e839a9f0b5259d047547a82 $7.46 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/14 * 7695b47e54ee15e8a98913dc24a4c72ef8dc8be1 9e67321982e83628563e8a2b396325aa18283ba5 $182.19 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/14 * 6a186a6a77b18206d98f2f4f875090a7c96ed118 9c484b5dc87055f93751ad00947fd9a7a14ea470 $19.47 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/15 * 4f70f05616f803772e7632a21030fdb33506c5f6 1b565047893eb8f55e839a9f0b5259d047547a82 $20.71 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/16 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $979.13 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/01/16 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $5.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/16 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $15.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/17 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $11.14 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/17 * 50ddf3adbb7dba10abde203694dfe0b77cad138a 92a772d9a491a8c8f239d9148b979f1da7369480 $8.59 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/18 * 5cc3303576abdd30cc701f23fab05d15443ff6c1 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.69 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/17 * bc9899328c1c50c76dfa5068f02178b479ead0d2 168eba32f6a6113fe447cd8516aa4d3544170084 $117.67 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/19 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $2.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-102.00 2004/01/19 * 4d565ed871c3d07f2878d73e18576ceb83f8700b 1b565047893eb8f55e839a9f0b5259d047547a82 $9.61 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/20 * d684e253a5be6ce373861f2ef69a37a10b0caaec c233d176ce06c06ecfd032230c4be5ff4476a554 $114.75 1237fd153ab3077a51de74b5e659441b7bf6ef01 $13.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-128.25 2004/01/20 * (2142) 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $28.04 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/01/20 * (2143) a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $16.86 9c484b5dc87055f93751ad00947fd9a7a14ea470 $16.84 cecae7f2312046d2775a401cc3c3925b79676ce3 $0.70 e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $29.95 e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $29.95 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-94.30 2004/01/20 * (2144) dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $154.22 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/01/20 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $21.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/22 * (2145) b417958a5c0b64878801a502f469cf3687c02bb9 59284ef86feceb946c427aeb6c0badfeb415b446 $90.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/01/23 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $10.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/26 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $12.28 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/27 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $15.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/27 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $23.78 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/29 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $6.12 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/30 * d684e253a5be6ce373861f2ef69a37a10b0caaec 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $33.85 1237fd153ab3077a51de74b5e659441b7bf6ef01 $5.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-39.25 2004/01/30 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df c233d176ce06c06ecfd032230c4be5ff4476a554 $31.34 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/30 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.94 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/30 * 4f70f05616f803772e7632a21030fdb33506c5f6 1b565047893eb8f55e839a9f0b5259d047547a82 $15.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/01/31 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $98.02 5c40e29310f67c1d38cd0a6251819dc2860aab37 2004/01/31 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/01 * (2146) 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/02/02 * d94a59cd70d2ae2ec1601cdfae3035a038f44bdc 1b565047893eb8f55e839a9f0b5259d047547a82 $5.35 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/03 * (2147) 8c08837348c1f28a7cc8547c98418ddf1344a6b3 602986d0a44a1e0a1ab7f11691c70a1219131721 $24.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/02/03 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $14.35 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/03 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $8.85 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-58.85 2004/02/04 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/05 * 0760a28df0204fe7687476a3d0692abea12019d0 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $250.00 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2004/02/05 * 9041a4117789337045dfc4e608b5442192753977 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $50.00 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2004/02/05 * (2148) a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $46.81 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/02/05 * (2149) dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $45.75 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/02/05 * (2150) 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 9c484b5dc87055f93751ad00947fd9a7a14ea470 $23.67 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/02/05 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $13.60 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/07 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $6.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/08 * e2045ef4a45bbf00e03b4b0f12d96980790ff80b 1b565047893eb8f55e839a9f0b5259d047547a82 $6.87 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/08 * fe598f4e16bedbd2e3f7a2e0f78d7dc245671b81 c233d176ce06c06ecfd032230c4be5ff4476a554 $11.98 1b565047893eb8f55e839a9f0b5259d047547a82 $8.21 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-20.19 2004/02/12 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $2.97 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/12 * 316d046d288bef78213732e7e794f35dbe729ae3 1b565047893eb8f55e839a9f0b5259d047547a82 $109.97 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/14 * 713e03f61b59ddb873417498b33c61be1b3f44f5 1b565047893eb8f55e839a9f0b5259d047547a82 $12.60 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/14 * 7bd0f94e64d8a7deb15f2742228c01641e9b9a84 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $94.71 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/15 * 9af14dbc27d9b233cc6a2153d1235179faed6d9d 1b565047893eb8f55e839a9f0b5259d047547a82 $22.12 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/16 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $1,489.05 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/02/16 * 39ccba9779f59020ae45a9df08864376a1011b80 1b565047893eb8f55e839a9f0b5259d047547a82 $15.94 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/17 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $39.07 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/17 * 1cba536dbd4df3904a3d93f41c3967d07c56dcaa 1b565047893eb8f55e839a9f0b5259d047547a82 $17.75 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/18 * 9eda6f7f52f1168acdc85451c3f293fb932f8a7a 1b565047893eb8f55e839a9f0b5259d047547a82 $45.27 cfd76529eda7575c434ab6edd70e56693f979bb1 $30.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-75.27 2004/02/19 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-101.50 2004/02/19 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $17.58 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/20 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $6.12 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/20 * 953388f315fdab9d0166aaa871ca84fc212af6fd 1b565047893eb8f55e839a9f0b5259d047547a82 $6.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/21 * e35225a98709b6e8dd0f331b96cc0d0a794b9cad 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $17.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/21 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $31.37 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/22 * ce6771d870502fe52ab8ae77d8c7cab02879fde6 1b565047893eb8f55e839a9f0b5259d047547a82 $101.00 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-121.00 2004/02/23 * 6e011e2dd9b3f7b54c048e6b873c6242d6aaad3d 1b565047893eb8f55e839a9f0b5259d047547a82 $7.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/23 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $31.58 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/26 * 46a7a458be73830dbf0160c876fb19f963bc733c 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.76 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/26 * c24a372e6acb6fb088a73bb23c4c625522c56dd4 1b565047893eb8f55e839a9f0b5259d047547a82 $21.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/29 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $82.91 5c40e29310f67c1d38cd0a6251819dc2860aab37 2004/02/29 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $29.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/02/29 * 95b6b46bf9f262cf7aeffa04ba12d3bde9411eb6 1b565047893eb8f55e839a9f0b5259d047547a82 $20.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/03/01 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $26.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/03/01 * (2151) 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/01 * (2152) b417958a5c0b64878801a502f469cf3687c02bb9 463628a20f371d71d46a7947f1175a0c16ce2f45 $250.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/01 * 7994a463ade0f972bf1bb59ac236e37b08bfc4d7 628072f9bfc87daeb5e0144ef2090ef8057935e8 $181.74 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/02 * cbb4cc49824bf79827cde838e005848027ca0a38 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $5,000.00 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 2004/03/02 * 1b0cfd69357b7c20fb55adfc8fe5afa6ddffe80a 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $41.94 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/02 * 49bf3e3ca59241e4066bb3a50ea8d6564f303217 1b565047893eb8f55e839a9f0b5259d047547a82 $6.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/03 * 9ac21a761bd6c9a47860f1dfc51a37dea68b051d 1b565047893eb8f55e839a9f0b5259d047547a82 $17.68 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/04 * c50ffe9fd9ab159d36900b2a90d0db4ddf70311e 92a772d9a491a8c8f239d9148b979f1da7369480 $16.68 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/05 * b5a53b07e5ff84d945366dde1a100710e7bc2c2c 1b565047893eb8f55e839a9f0b5259d047547a82 $12.48 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/07 * 95b6b46bf9f262cf7aeffa04ba12d3bde9411eb6 1b565047893eb8f55e839a9f0b5259d047547a82 $42.02 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/08 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $26.58 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/08 * 00fd0c8b4c6e05aee1fb76b2b961265f0dea3168 1b565047893eb8f55e839a9f0b5259d047547a82 $12.88 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/08 * af636338204eb20a819cb1da004e0332d82b995e dc11d67d6cc4ca6136c8690e387e70772dcb8465 $9.68 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/09 * 39ccba9779f59020ae45a9df08864376a1011b80 1b565047893eb8f55e839a9f0b5259d047547a82 $7.95 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/09 * b6d03fa2a031c97515cf8e68ab32b80b8df4b658 1b565047893eb8f55e839a9f0b5259d047547a82 $19.03 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/09 * 478b8cf87b83049bacb3b8d81693a1a9ee1a2af9 f2580c2fa4873496427487e068658993bbf70894 $4.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/09 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $2.19 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-52.19 2004/03/10 * 6311a5e415b6218c88c877cf57d459a79a646a22 92a772d9a491a8c8f239d9148b979f1da7369480 $25.86 6d93163434f5b2253b3b5283f6015b60c79ea659 $29.90 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-55.76 2004/03/10 * 40394c9f4ae1653ba4c7ef61eb70265bb119f227 f2580c2fa4873496427487e068658993bbf70894 $17.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/11 * e7e72b84f250cb56f3c7128b1c09a947b1aa03b7 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $12.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/03/11 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $18.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/13 * f3b7a362fa81544cf535165b88396e0a4a241c73 1b565047893eb8f55e839a9f0b5259d047547a82 $72.33 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/13 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $23.47 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/14 * ac52be5218b8d0b39697630e541f12d42c6ee135 1b565047893eb8f55e839a9f0b5259d047547a82 $49.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/14 * 2b474fcd668caa0d26b44adc77d410ac5e257bfd 1b565047893eb8f55e839a9f0b5259d047547a82 $11.23 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/15 * 624de2e9a0226301c5a73ed4a6cb48dc23ca03a7 1b565047893eb8f55e839a9f0b5259d047547a82 $22.33 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/16 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $728.85 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/16 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $13.72 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/16 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/03/17 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $12.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/18 * 299eb28835cafffe80dbb8398c478bd6b35b34bf 1b565047893eb8f55e839a9f0b5259d047547a82 $5.06 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/18 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $26.09 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/18 * 729f7e00eb1e3dae7fd7fc9cc1af67af493444f7 1b565047893eb8f55e839a9f0b5259d047547a82 $4.99 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/21 * 8f2a9369c196f88970181565a81aab1d8816b126 1b565047893eb8f55e839a9f0b5259d047547a82 $19.13 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/21 * 478b8cf87b83049bacb3b8d81693a1a9ee1a2af9 f2580c2fa4873496427487e068658993bbf70894 $4.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/22 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df c233d176ce06c06ecfd032230c4be5ff4476a554 $9.67 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/22 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 c233d176ce06c06ecfd032230c4be5ff4476a554 $8.44 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/24 * 729f7e00eb1e3dae7fd7fc9cc1af67af493444f7 1b565047893eb8f55e839a9f0b5259d047547a82 $15.92 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/25 * 2971f8982324038e1c21d1928aa537c3e9d461af 1b565047893eb8f55e839a9f0b5259d047547a82 $30.77 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/25 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df c233d176ce06c06ecfd032230c4be5ff4476a554 $7.10 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/26 * 40394c9f4ae1653ba4c7ef61eb70265bb119f227 f2580c2fa4873496427487e068658993bbf70894 $15.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/27 * 45d86d00b9617c9c8e82caa507fb718473c44e09 1b565047893eb8f55e839a9f0b5259d047547a82 $65.67 cfd76529eda7575c434ab6edd70e56693f979bb1 $35.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-100.67 2004/03/27 * 16eefd46b8e0282071752465137863f6347f7d44 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.68 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/27 * d245e9f1800efaf04f61c3a9a4385d5ed151ea6a 1b565047893eb8f55e839a9f0b5259d047547a82 $9.56 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/28 * 216285097606392fb75c043c9155558b6ddbcaf5 c233d176ce06c06ecfd032230c4be5ff4476a554 $5.37 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/28 * d7c9211568d4e88fab0fadadc5cef842e8fc7021 168eba32f6a6113fe447cd8516aa4d3544170084 $60.25 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/28 * efc0cd23af4297386affbde9273a67913b0e9c49 1b565047893eb8f55e839a9f0b5259d047547a82 $13.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/29 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df 1b565047893eb8f55e839a9f0b5259d047547a82 $14.62 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/30 * b23e1b7382f471ee532d7389a885b3982712f53e 1b565047893eb8f55e839a9f0b5259d047547a82 $36.27 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/31 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $77.09 5c40e29310f67c1d38cd0a6251819dc2860aab37 2004/03/31 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.94 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-53.94 2004/03/31 * b1192f2834401dc16af8b64643ae985c02cb4961 1b565047893eb8f55e839a9f0b5259d047547a82 $7.87 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/31 * c3fc7cbedf732de88ceab7f7fa34a592021d7024 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.13 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/03/31 * c3fc7cbedf732de88ceab7f7fa34a592021d7024 1b565047893eb8f55e839a9f0b5259d047547a82 $8.57 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/01 * 3c282ee44658366f1ad6b49c76f74038beedaa90 1b565047893eb8f55e839a9f0b5259d047547a82 $23.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/01 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $32.39 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/02 * be95ab7efd9f203a652c73b9031c74106f994112 181884ada86d2c3e7511e3ef3830fcf9f75b1421 $7.40 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/02 * d66f20839763a4d86071659cdcbebaa6020203c3 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.17 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/02 * (2153) 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/02 * (2154) 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $15.13 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/02 * (2155) a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $114.76 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/02 * (2156) dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $87.94 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/02 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 6d93163434f5b2253b3b5283f6015b60c79ea659 $31.98 1b565047893eb8f55e839a9f0b5259d047547a82 $11.42 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-93.40 2004/04/02 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $9.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/04/03 * 22ab203c3ad837c0bb91907851ec7c9260999120 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $526.94 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/03 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $25.05 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/04 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/05 * fafc3ee2608e739320f4cbe2db38fb45c30f64d0 6d93163434f5b2253b3b5283f6015b60c79ea659 $54.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/05 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $13.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/06 2cbbc7f08aada2c9338bb1bc0e6dc05ed5fe76e1 1b565047893eb8f55e839a9f0b5259d047547a82 $15.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/04/06 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $21.24 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/07 * c4a01aa5c095dd8265c08b813276d84ac0788901 a7cefcc26daa42c746631597e921d13810dd1e5d $64.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/07 * 0760a28df0204fe7687476a3d0692abea12019d0 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $250.00 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2004/04/07 * bea8d5e2678e516790a1d65f5c22875c1ee81f09 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $100.00 ff7d6181c581373db166118e7fd34bfa6f3f2dcb 2004/04/07 * 335a0f6841267b99a0a147100392fed47b80e597 9e67321982e83628563e8a2b396325aa18283ba5 $313.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/08 * c744bd69d5739cfe69ec89b0d0435a3ef4cdb1b0 9e67321982e83628563e8a2b396325aa18283ba5 $99.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/08 * d684e253a5be6ce373861f2ef69a37a10b0caaec c233d176ce06c06ecfd032230c4be5ff4476a554 $150.45 1237fd153ab3077a51de74b5e659441b7bf6ef01 $17.10 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-167.55 2004/04/08 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $2.97 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/08 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $60.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-61.50 2004/04/08 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.75 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/10 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $9.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/04/10 * 0bdfa162041573731f35ed9a115cebcceba07a68 592a60e960113a755f70f9b58ef7a97b5aca14dc $69.90 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/10 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $1.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/10 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $15.84 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/11 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $12.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/11 * (2157) b8b586cfa1b2274a0bf946e5b9e53e92c185067c e940525b878f1783a30bf0cd9c040f6d27e0568c $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/13 * e800eec67b4c03b445b61819cf8e1f7b27916f45 b70cb9532a007c1320479c559989bd9b7fc579c1 $272.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/13 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $25.41 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/14 * 0ca848f92d157881e94b313d91f9ecf364b3713a a7cefcc26daa42c746631597e921d13810dd1e5d $19.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/15 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/15 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $655.43 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/15 * ce6771d870502fe52ab8ae77d8c7cab02879fde6 1b565047893eb8f55e839a9f0b5259d047547a82 $29.64 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/16 * 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $4.90 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/16 * a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $85.33 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/16 * bb8b805afd42daedeadec2cd90fbfc10211ea0ea 5ea6ff037c50e85215211c1c1a25eebf6014611e $146.16 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/16 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $7.84 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/18 * d66f20839763a4d86071659cdcbebaa6020203c3 3282f21c97a0e1f66185923328d80d87fa5d8db7 $30.11 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/18 * (2158) 50ddf3adbb7dba10abde203694dfe0b77cad138a 1b565047893eb8f55e839a9f0b5259d047547a82 $10.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/18 * f159c0ab1d78ac3ddcd8264eed66b66516c7dd09 1b565047893eb8f55e839a9f0b5259d047547a82 $6.36 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/19 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $1.79 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/19 * 785d798d8a4706d97cb3bbea3041d23aa12bedad 1b565047893eb8f55e839a9f0b5259d047547a82 $16.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/20 * 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/20 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $17.72 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-67.72 2004/04/20 * 3c0a1c540b2499a98fc4f07ff7e8bc35a0c4921e 1b565047893eb8f55e839a9f0b5259d047547a82 $22.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/20 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/21 * 4f36cda4f67b75a4ef1e867a0a51313233c04879 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $8,103.00 3d81529f3a4a694e57bbd6e63ffed2215bdef336 2004/04/21 * 2a9649e01b659d9afcee414f35ac009fb43eed0c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $188.00 3d81529f3a4a694e57bbd6e63ffed2215bdef336 2004/04/21 * 504bbaf175bfba4956ef5dc3a38e1ff693874597 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $6,208.32 eb0c1e3629fe7cba500081ef756a72e9659a93c4 2004/04/21 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be fc0e191163be4d1966e3c51b1635401f9e82a807 70 AAPL fc6f6f10f627ad1a5af9d488c98405a1498d019d $-5,000.00 2004/04/21 * b45923836563f437a6394be8f4fd035bf7145f8d fc0e191163be4d1966e3c51b1635401f9e82a807 -70 AAPL f0d45665b22d0562833aa3bf373c5b15640d833e $-955.98 49c6eb709b3d1613e4d6a1c04ee0ed9d23d665a4 $955.98 fc0e191163be4d1966e3c51b1635401f9e82a807 $1,911.96 2004/04/21 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $8.32 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/22 * f1a1dcb303695a9107bef9686ec91b4fb3f2e19f eb0c1e3629fe7cba500081ef756a72e9659a93c4 $12.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/22 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.24 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/23 * (2159) 03642d4c8cfd39cbe8e1bbce3179a0ca53935e30 f0d45665b22d0562833aa3bf373c5b15640d833e $955.98 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/23 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $34.39 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/24 * 2204654c274e49b1a46ec1acbed7519645daddc6 1b565047893eb8f55e839a9f0b5259d047547a82 $33.89 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/25 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.79 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-53.79 2004/04/25 * a2fbb8ea26447c5ebd4f3de68a596027cc489649 1b565047893eb8f55e839a9f0b5259d047547a82 $43.70 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/26 * 20afab0a20b954305e459443a939ad2e51bc30ab 59284ef86feceb946c427aeb6c0badfeb415b446 $26.41 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/25 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $19.01 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/26 * cbb4cc49824bf79827cde838e005848027ca0a38 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $1,913.00 fc0e191163be4d1966e3c51b1635401f9e82a807 2004/04/26 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $9.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/04/26 * 612979bcaf308370105ac99e7dbbb8098f5b5b27 1b565047893eb8f55e839a9f0b5259d047547a82 $17.02 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/27 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/27 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/28 * dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $44.18 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/28 * 91c59df073c8abd416047acbac4f38dd4834d955 1b565047893eb8f55e839a9f0b5259d047547a82 $7.16 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/04/28 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $10.04 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/29 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/30 * 6a186a6a77b18206d98f2f4f875090a7c96ed118 9e67321982e83628563e8a2b396325aa18283ba5 $356.83 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $956.20 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-1,313.03 2004/04/30 * f7dfe4fbd5e0b9027298ac1a5543cc0f5c2a2a30 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $86.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/30 * f7dfe4fbd5e0b9027298ac1a5543cc0f5c2a2a30 6d93163434f5b2253b3b5283f6015b60c79ea659 $44.76 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/04/30 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $74.83 5c40e29310f67c1d38cd0a6251819dc2860aab37 2004/05/01 * 504bbaf175bfba4956ef5dc3a38e1ff693874597 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $2,916.66 39189083b8637c7fff89e6bcf808790861417796 2004/05/01 * (2160) 619cbfd21d72639f985324a9306696ce1262f3f6 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $13,000.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/01 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $4.89 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/03 * 07a24fc3a072f588a7498d8b94a37e0e587632dc 1b565047893eb8f55e839a9f0b5259d047547a82 $20.68 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/03 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $32.02 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/04 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $15.84 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-65.84 2004/05/04 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/05 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $33.68 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/05 * 3c0a1c540b2499a98fc4f07ff7e8bc35a0c4921e 1b565047893eb8f55e839a9f0b5259d047547a82 $17.05 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/06 * 02eeeedda75b06ccf47220e7020c8e1b705d67a4 cecae7f2312046d2775a401cc3c3925b79676ce3 $10.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/07 * (2161) 8c08837348c1f28a7cc8547c98418ddf1344a6b3 39ee34a6410884ea66b9f331c91f8383e2e592f0 $119.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/07 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/08 * (2162) 131a0c60d087a611e0a31601db68125e5a2dd4fc 1b565047893eb8f55e839a9f0b5259d047547a82 $22.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/09 * 275cc7006db94d75505db825dc5542aed9bfc26c fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $1,236.19 0ecbb1b15e2cf3e515cc0f8533e5bb0fb2326728 2004/05/09 * 9edce3fdc14e578091c4e785950f76c1691ac7a6 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $39.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/09 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $25.61 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-45.61 2004/05/10 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $34.14 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/10 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $22.32 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/10 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $3.18 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/10 * cbb4cc49824bf79827cde838e005848027ca0a38 c56a21d23a6535184e7152ee138c28974f14280c 2,242.324241 BBBBB @ $22.7300000000000000000173472348 c56a21d23a6535184e7152ee138c28974f14280c 2,558.818182 DDDDD @ $8.9099999999999999998612221219 c56a21d23a6535184e7152ee138c28974f14280c 604.908255 FFFFF @ $41.3099999999999999999479582957 c56a21d23a6535184e7152ee138c28974f14280c -2,084.582278 AAAAA @ $24.4499999999999999972244424384 c56a21d23a6535184e7152ee138c28974f14280c -387.278233 LMVTX @ $58.8699999999999999998959165914 c56a21d23a6535184e7152ee138c28974f14280c -936.961582 GGGGG @ $26.6700000000000000000693889390 c56a21d23a6535184e7152ee138c28974f14280c $0.01 2004/05/11 * 106e6bd420d331275023c76038ebb520502b39ef 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/12 * 729f7e00eb1e3dae7fd7fc9cc1af67af493444f7 1b565047893eb8f55e839a9f0b5259d047547a82 $26.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/12 * eb876715e4e911d1b72da43fa6e021225ac74ce0 1b565047893eb8f55e839a9f0b5259d047547a82 $8.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/13 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/13 * 26594b5abbc4e9dd869f14f1df59d43ebb4b91b6 1b565047893eb8f55e839a9f0b5259d047547a82 $23.17 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/13 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $30.35 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/14 * f0c8ce7bc712cf25564004a6ab5a08be96d94504 1b565047893eb8f55e839a9f0b5259d047547a82 $53.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/14 * 40394c9f4ae1653ba4c7ef61eb70265bb119f227 f2580c2fa4873496427487e068658993bbf70894 $17.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/15 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $1,732.25 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/15 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $4.05 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/16 * 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $17.21 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/16 * a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $84.51 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/16 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $7.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/16 * 95b6b46bf9f262cf7aeffa04ba12d3bde9411eb6 1b565047893eb8f55e839a9f0b5259d047547a82 $41.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/17 * 1e6df810ce83206bb4c563047d86d2a0b9765e65 1b565047893eb8f55e839a9f0b5259d047547a82 $13.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/17 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $28.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/17 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $9.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/17 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $40.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-41.50 2004/05/18 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.69 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/18 * 9401381d21c8b210d51407a7281b2979c6934bbf 1b565047893eb8f55e839a9f0b5259d047547a82 $15.41 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/18 * 6311a5e415b6218c88c877cf57d459a79a646a22 1b565047893eb8f55e839a9f0b5259d047547a82 $8.03 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/18 * 6311a5e415b6218c88c877cf57d459a79a646a22 6d93163434f5b2253b3b5283f6015b60c79ea659 $10.51 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/18 * 0fb419d273cdd0cfe9825e94f91c2058e99c1f7a 1b565047893eb8f55e839a9f0b5259d047547a82 $21.68 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/18 * c50ffe9fd9ab159d36900b2a90d0db4ddf70311e 92a772d9a491a8c8f239d9148b979f1da7369480 $3.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/19 * 6311a5e415b6218c88c877cf57d459a79a646a22 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $23.13 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/19 * (2163) db96a59e17a29aba685420d1f72271cb0e2d4be2 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $37.12 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/19 * 0fb419d273cdd0cfe9825e94f91c2058e99c1f7a 1b565047893eb8f55e839a9f0b5259d047547a82 $17.10 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/19 * 478b8cf87b83049bacb3b8d81693a1a9ee1a2af9 f2580c2fa4873496427487e068658993bbf70894 $4.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/20 * 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/20 * b6d03fa2a031c97515cf8e68ab32b80b8df4b658 1b565047893eb8f55e839a9f0b5259d047547a82 $11.19 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/20 * f7dfe4fbd5e0b9027298ac1a5543cc0f5c2a2a30 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $11.27 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/21 * b1b1ed463af52a975722e23887c94b652b4ad7e2 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $15.32 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/21 * 61ec80beb9a6f44903c8f6559f7051962cbac447 1b565047893eb8f55e839a9f0b5259d047547a82 $3.38 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/21 * 8f2a9369c196f88970181565a81aab1d8816b126 1b565047893eb8f55e839a9f0b5259d047547a82 $9.02 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/21 * 97a7a570c8ede9be3bb8ca6edb1ee14e1012c595 1b565047893eb8f55e839a9f0b5259d047547a82 $6.65 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/21 * f7dfe4fbd5e0b9027298ac1a5543cc0f5c2a2a30 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $3.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/21 * 015c9e35cf2dcd43a6dc42ab8ee65dc48bf89b91 1b565047893eb8f55e839a9f0b5259d047547a82 $19.14 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/22 * 49bf3e3ca59241e4066bb3a50ea8d6564f303217 1b565047893eb8f55e839a9f0b5259d047547a82 $23.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/22 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $23.01 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/22 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.50 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-101.50 2004/05/23 * 2172039ae1b9ef750b090fbe98e8b3fdc1a3553e 1b565047893eb8f55e839a9f0b5259d047547a82 $26.86 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/23 * 6311a5e415b6218c88c877cf57d459a79a646a22 1b565047893eb8f55e839a9f0b5259d047547a82 $6.80 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/23 * 771469322c94ab3cb0b03e21018f3aa2b81abb11 1b565047893eb8f55e839a9f0b5259d047547a82 $10.58 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/25 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $34.39 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/25 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 1b565047893eb8f55e839a9f0b5259d047547a82 $5.38 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/25 * 61ad50a9b9189cc3cf1874568e35e7901ff4c982 1b565047893eb8f55e839a9f0b5259d047547a82 $8.01 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/26 * d66f20839763a4d86071659cdcbebaa6020203c3 3282f21c97a0e1f66185923328d80d87fa5d8db7 $32.26 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/26 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $9.71 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/26 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.49 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/27 * 06e1515d7acf1e9b3869e82b570fd55c60ed97c2 5ea6ff037c50e85215211c1c1a25eebf6014611e $13.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/27 * a46af6931d9dace2200617548fab3274549e308f 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $58.70 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/27 26bb0e8d147d95e73f96c919636363ebc9d53037 (8ccfbea4d5d39235320ffeffe845cb68ef297cb9) $256.90 2004/05/28 * 1c1bb064786be7eb3461145f0c18b033b1ffe9ef 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $329.72 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/28 * 78565e2e4c37cefe941dd5fe4adec3e71f1b3456 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $95.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/28 * f159c0ab1d78ac3ddcd8264eed66b66516c7dd09 1b565047893eb8f55e839a9f0b5259d047547a82 $11.34 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/28 * 00d188d123568c8992f208c9566d50bdc9d426ee fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $0.41 ff7d6181c581373db166118e7fd34bfa6f3f2dcb $-0.30 ff7d6181c581373db166118e7fd34bfa6f3f2dcb $-0.11 2004/05/29 * a2fbb8ea26447c5ebd4f3de68a596027cc489649 1b565047893eb8f55e839a9f0b5259d047547a82 $34.17 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/30 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/30 * 953388f315fdab9d0166aaa871ca84fc212af6fd 1b565047893eb8f55e839a9f0b5259d047547a82 $8.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/05/30 * (2165) 131a0c60d087a611e0a31601db68125e5a2dd4fc 1b565047893eb8f55e839a9f0b5259d047547a82 $15.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/05/31 * 09462753ff102b976a33e75fdde093d4ad6e39ca 1b565047893eb8f55e839a9f0b5259d047547a82 $14.04 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-34.04 2004/05/31 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $90.65 5c40e29310f67c1d38cd0a6251819dc2860aab37 2004/06/01 * d66f20839763a4d86071659cdcbebaa6020203c3 3282f21c97a0e1f66185923328d80d87fa5d8db7 $30.88 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/01 * dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $44.18 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/01 * 9675f80d41147aee96475d47554f4b0de421ad4e 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $58.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/01 * 5204ec41dca3d276c560c5baa59f2e15aad5efc5 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $103.99 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/01 * d682d553b633ed402c13806e6d05f3282c30dc47 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $24.94 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/01 * a1bd7d7413519862e1950f81b1b947c19374cb4a dc11d67d6cc4ca6136c8690e387e70772dcb8465 $12.00 dc11d67d6cc4ca6136c8690e387e70772dcb8465 $9.50 dc11d67d6cc4ca6136c8690e387e70772dcb8465 $21.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-42.50 2004/06/01 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/01 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/01 * 612979bcaf308370105ac99e7dbbb8098f5b5b27 1b565047893eb8f55e839a9f0b5259d047547a82 $15.12 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-35.12 2004/06/01 * 94feb6c59fff720bb427d719c5086944b11ecfe5 cecae7f2312046d2775a401cc3c3925b79676ce3 $1.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/02 * b39b0c179cab6038532c65b62310ee775d65dd94 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $99.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/02 * c4cd18eae2f9b743b28811ddc30c87ad961c46af 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $204.97 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/02 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $28.91 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/03 * a91ab933c971ab9782b57b39377fc6b9aa878294 dc11d67d6cc4ca6136c8690e387e70772dcb8465 $21.11 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/03 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $12.01 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/04 * 1bbbcf3a6fcd46c18d81d70cab39387674500013 39ee34a6410884ea66b9f331c91f8383e2e592f0 $108.24 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/04 * 504bbaf175bfba4956ef5dc3a38e1ff693874597 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $2,916.66 39189083b8637c7fff89e6bcf808790861417796 2004/06/04 * 504bbaf175bfba4956ef5dc3a38e1ff693874597 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $138.50 eb0c1e3629fe7cba500081ef756a72e9659a93c4 2004/06/04 * (2166) cb366c5b066ce919fb87eabefef0556bbfaf81c0 2ff50cab09d039eaa717cac29cf2759eb39a007d $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/05 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $21.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/05 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $14.71 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/06 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $12.28 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/07 * c4cd18eae2f9b743b28811ddc30c87ad961c46af dc11d67d6cc4ca6136c8690e387e70772dcb8465 $9.04 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/07 * c4cd18eae2f9b743b28811ddc30c87ad961c46af dc11d67d6cc4ca6136c8690e387e70772dcb8465 $20.99 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/07 * (2167) 74c62adeb5902bbcee5761fd626e81958b18170d 602986d0a44a1e0a1ab7f11691c70a1219131721 $152.25 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/10 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $25.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/10 * 6a186a6a77b18206d98f2f4f875090a7c96ed118 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $43.29 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/11 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/12 * 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $2.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-102.00 2004/06/12 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $27.13 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/13 * 612979bcaf308370105ac99e7dbbb8098f5b5b27 1b565047893eb8f55e839a9f0b5259d047547a82 $18.91 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/13 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $17.67 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/14 * f1a1dcb303695a9107bef9686ec91b4fb3f2e19f eb0c1e3629fe7cba500081ef756a72e9659a93c4 $12.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/14 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $31.34 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/15 * 2c8ac236bcc8813497becd0e65a0aba085b05606 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $30.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/15 * 2c8ac236bcc8813497becd0e65a0aba085b05606 326b155986fe005914c2cd52851da075ff65992f $18.13 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/15 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $2,342.71 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/15 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $10.83 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/16 * 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $17.44 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/16 * a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $83.44 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/18 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/19 * 1fc8bdc3f19a570ae59f18f676c770d9ef0ae27a dc11d67d6cc4ca6136c8690e387e70772dcb8465 $14.49 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/19 * 1fc8bdc3f19a570ae59f18f676c770d9ef0ae27a dc11d67d6cc4ca6136c8690e387e70772dcb8465 $11.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/19 * a46af6931d9dace2200617548fab3274549e308f 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $28.49 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/19 * a91ab933c971ab9782b57b39377fc6b9aa878294 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $10.83 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/19 * b422694a3e6330fa33f2996acb27cc83093c1cdf dc11d67d6cc4ca6136c8690e387e70772dcb8465 $9.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/19 * 61ad50a9b9189cc3cf1874568e35e7901ff4c982 dc11d67d6cc4ca6136c8690e387e70772dcb8465 $9.17 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/20 * 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/20 * 6311a5e415b6218c88c877cf57d459a79a646a22 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $25.87 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/20 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $8.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/21 * (2168) b8b586cfa1b2274a0bf946e5b9e53e92c185067c e940525b878f1783a30bf0cd9c040f6d27e0568c $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/06/21 * be95ab7efd9f203a652c73b9031c74106f994112 181884ada86d2c3e7511e3ef3830fcf9f75b1421 $8.82 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/21 * a49dd639ccbaa09aa85d8426ade6e5ffad910f49 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $3.33 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/21 * a49dd639ccbaa09aa85d8426ade6e5ffad910f49 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $9.17 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/21 * 09462753ff102b976a33e75fdde093d4ad6e39ca 1b565047893eb8f55e839a9f0b5259d047547a82 $7.49 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/21 * a95c40f310c47178537882d69dcfe6b8b1dcc080 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $2.71 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/21 * 987f1f9e420a473ad352b7ed9edf01e112842177 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $6.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/21 * aa5653a7a2f2eee41b9af0acf35848fa1e00bf34 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $5.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/21 * bebe255c0d6885334049a7c26159237f1a815bbc 2d059a4c9183c0ad073f02076c5184c8fa8eaed6 $77.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/21 * 6a186a6a77b18206d98f2f4f875090a7c96ed118 8ccfbea4d5d39235320ffeffe845cb68ef297cb9 $32.46 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/22 * 3c0a1c540b2499a98fc4f07ff7e8bc35a0c4921e 1b565047893eb8f55e839a9f0b5259d047547a82 $23.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/23 * 3d363ed8ccc251083432b152f1b4c9c2d82f8941 1b565047893eb8f55e839a9f0b5259d047547a82 $19.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/23 * 1cdc2c2d06318a1f26e380931650cc576f462076 ca268d538b1a0056c1e3c8c5874d4cb30452d738 $28.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/23 * a2fbb8ea26447c5ebd4f3de68a596027cc489649 1b565047893eb8f55e839a9f0b5259d047547a82 $59.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/24 * 5186baea3db85c63ab38c4698daa14a530670c4a 1b565047893eb8f55e839a9f0b5259d047547a82 $4.53 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/24 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $33.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/25 * 1cdc2c2d06318a1f26e380931650cc576f462076 ca268d538b1a0056c1e3c8c5874d4cb30452d738 $56.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/25 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $23.97 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/26 * 1cdc2c2d06318a1f26e380931650cc576f462076 ca268d538b1a0056c1e3c8c5874d4cb30452d738 $28.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/26 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $8.88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/26 * 1cdc2c2d06318a1f26e380931650cc576f462076 ca268d538b1a0056c1e3c8c5874d4cb30452d738 $28.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/27 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $1.41 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/27 * 9861ce541c17b11f627e71c26bf350b33141f62b 1b565047893eb8f55e839a9f0b5259d047547a82 $8.31 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/27 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $20.76 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/28 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $15.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/28 * ab4621385974a232c17c229d8b2b4eb75f13ba51 1b565047893eb8f55e839a9f0b5259d047547a82 $27.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/29 * 6e011e2dd9b3f7b54c048e6b873c6242d6aaad3d 1b565047893eb8f55e839a9f0b5259d047547a82 $16.46 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/29 * 7b3318b6fa2d182878fd4fa214b2b2d16d122d5e 1b565047893eb8f55e839a9f0b5259d047547a82 $27.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/30 * d2667e42a8beac84d6f95887ab807a947a425d67 1b565047893eb8f55e839a9f0b5259d047547a82 $28.89 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/30 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $32.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/06/30 * 3a12015d49db73ea5d5dcdf3d749b49b3a0240ad 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 $89.48 5c40e29310f67c1d38cd0a6251819dc2860aab37 2004/07/01 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $28.11 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-78.11 2004/07/01 * 504bbaf175bfba4956ef5dc3a38e1ff693874597 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $2,916.66 39189083b8637c7fff89e6bcf808790861417796 2004/07/01 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/01 * 2c8ac236bcc8813497becd0e65a0aba085b05606 326b155986fe005914c2cd52851da075ff65992f $34.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/01 * 2c8ac236bcc8813497becd0e65a0aba085b05606 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $57.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/01 * 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/02 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $23.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/03 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $16.12 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/03 * 612979bcaf308370105ac99e7dbbb8098f5b5b27 1b565047893eb8f55e839a9f0b5259d047547a82 $28.49 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/05 * 3c0a1c540b2499a98fc4f07ff7e8bc35a0c4921e 1b565047893eb8f55e839a9f0b5259d047547a82 $18.05 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/05 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $6.16 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/06 * f7dfe4fbd5e0b9027298ac1a5543cc0f5c2a2a30 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $429.11 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/06 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $18.16 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/07 * 5d143d62e98809ac444b228114a6cfde901bad91 1b565047893eb8f55e839a9f0b5259d047547a82 $3.56 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/08 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $26.58 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-46.58 2004/07/09 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $34.49 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/09 * 48e6fffc4cd537c2c9a9ac6b751dc8674bcd00ba 1b565047893eb8f55e839a9f0b5259d047547a82 $27.24 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/11 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $19.18 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/12 * (2169) 25dfaea64340892890c1ca4d522b08ade0aeb689 2ff50cab09d039eaa717cac29cf2759eb39a007d $75.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/12 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $10.31 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/13 * 4d565ed871c3d07f2878d73e18576ceb83f8700b 1b565047893eb8f55e839a9f0b5259d047547a82 $10.97 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/14 * be95ab7efd9f203a652c73b9031c74106f994112 181884ada86d2c3e7511e3ef3830fcf9f75b1421 $4.90 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/14 * 27b88c0ce2f9724bd30a23beb098e01587e0c801 1b565047893eb8f55e839a9f0b5259d047547a82 $11.23 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/15 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $1,301.46 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/16 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $8.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/16 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $11.20 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/17 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $18.71 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-68.71 2004/07/18 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $7.45 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/18 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $33.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/18 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/19 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $9.95 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/19 * 7695b47e54ee15e8a98913dc24a4c72ef8dc8be1 3e2706db92ca6bb952333fd028e582695910c01d $173.20 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/19 * f1a1dcb303695a9107bef9686ec91b4fb3f2e19f eb0c1e3629fe7cba500081ef756a72e9659a93c4 $12.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/19 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.75 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/20 * 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/20 * 6ddb45499d05454b04e5102c359918a7ecc3eff7 602986d0a44a1e0a1ab7f11691c70a1219131721 $215.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/20 * fafc3ee2608e739320f4cbe2db38fb45c30f64d0 7134396063db3d3d81defdb1a2c68ee1383d199f $49.99 6d93163434f5b2253b3b5283f6015b60c79ea659 $36.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-86.14 2004/07/20 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $4.79 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/21 * a46af6931d9dace2200617548fab3274549e308f 92a772d9a491a8c8f239d9148b979f1da7369480 $22.32 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/21 * 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $23.80 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/21 * dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $44.18 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/21 * a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $79.91 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/21 * 6311a5e415b6218c88c877cf57d459a79a646a22 1b565047893eb8f55e839a9f0b5259d047547a82 $8.88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/21 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $15.49 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/23 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $19.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/23 * bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $12.82 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/23 * 47fee5bd794ca475684e9504bb438cd678916394 1b565047893eb8f55e839a9f0b5259d047547a82 $12.71 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/23 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $25.47 cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-125.47 2004/07/23 * 74c0fda1054b04bf3e2365d467e32a47e3feba7b 92a772d9a491a8c8f239d9148b979f1da7369480 $6.77 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/23 * 74c0fda1054b04bf3e2365d467e32a47e3feba7b 92a772d9a491a8c8f239d9148b979f1da7369480 $10.43 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/23 * 74c0fda1054b04bf3e2365d467e32a47e3feba7b 92a772d9a491a8c8f239d9148b979f1da7369480 $5.86 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/23 * 74c0fda1054b04bf3e2365d467e32a47e3feba7b 92a772d9a491a8c8f239d9148b979f1da7369480 $3.98 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/25 * 9861ce541c17b11f627e71c26bf350b33141f62b 3282f21c97a0e1f66185923328d80d87fa5d8db7 $20.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/25 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $11.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/26 * 98c898a0676fe2b3cb0ffff81413d0989eb8a2b8 602986d0a44a1e0a1ab7f11691c70a1219131721 $37.45 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/26 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $12.52 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/26 * a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/27 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $9.75 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/07/27 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $2.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/27 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $14.65 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/27 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $14.41 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/27 * 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $4.46 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/30 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $22.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/30 * 11d9eca019dbb06dc7cee01986ca2a4d43c30954 1b565047893eb8f55e839a9f0b5259d047547a82 $11.78 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/31 0b5722c2c27395bb81c2c6b548e3f5ae45180047 f2580c2fa4873496427487e068658993bbf70894 $10.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/07/30 * aadc967ebc7363b9ddadb35853de59ec10c09e72 e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $36.46 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/07/30 * e13b2a90bf864183a5ee1bf879ae41813eca623b e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $7.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/07/31 9fca7b8d5eb6cc9b0272edc33d05a50c77fa5589 f2580c2fa4873496427487e068658993bbf70894 $8.75 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/07/31 * b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $33.65 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/01 * b16891cc36681d41e678846933c29807a202e7c4 e5dfa7a292da1dd185fc7c6dd2402ec582a15f37 $5.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/01 * 504bbaf175bfba4956ef5dc3a38e1ff693874597 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $3,369.77 eb0c1e3629fe7cba500081ef756a72e9659a93c4 $-453.11 39189083b8637c7fff89e6bcf808790861417796 $-2,916.66 2004/08/01 * 2c8ac236bcc8813497becd0e65a0aba085b05606 326b155986fe005914c2cd52851da075ff65992f $34.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/01 * 2c8ac236bcc8813497becd0e65a0aba085b05606 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $57.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/02 * 612979bcaf308370105ac99e7dbbb8098f5b5b27 1b565047893eb8f55e839a9f0b5259d047547a82 $16.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/02 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $4.79 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-54.79 2004/08/03 * 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $14.11 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/03 * 2f7e9e35b7b925be61a94554e17b97e69de0e64b 1b565047893eb8f55e839a9f0b5259d047547a82 $9.07 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/04 * 835783fc1d053900e29b92be85e47024796c3ee5 1b565047893eb8f55e839a9f0b5259d047547a82 $20.27 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/05 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $5.18 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/05 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df 1b565047893eb8f55e839a9f0b5259d047547a82 $5.80 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/06 * 40394c9f4ae1653ba4c7ef61eb70265bb119f227 f2580c2fa4873496427487e068658993bbf70894 $17.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/06 * a54e655c93b4e8a987bd82d9a20d10e5078c091c 1b565047893eb8f55e839a9f0b5259d047547a82 $7.71 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/06 * 6311a5e415b6218c88c877cf57d459a79a646a22 1b565047893eb8f55e839a9f0b5259d047547a82 $3.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/06 * 6311a5e415b6218c88c877cf57d459a79a646a22 92a772d9a491a8c8f239d9148b979f1da7369480 $9.64 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/06 * afa70d90f551d4ff605bb4f4e0312ccae0ccb510 1b565047893eb8f55e839a9f0b5259d047547a82 $23.91 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/07 * cdbd6445b409d06d3bbeee9ad27c1d535ba7f6df c233d176ce06c06ecfd032230c4be5ff4476a554 $24.36 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/07 * 4d565ed871c3d07f2878d73e18576ceb83f8700b 1b565047893eb8f55e839a9f0b5259d047547a82 $8.18 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/08 * ac52be5218b8d0b39697630e541f12d42c6ee135 1b565047893eb8f55e839a9f0b5259d047547a82 $19.37 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/08 * 99da57dbbf75179d8ed5631fd0ccc588bd2c96d2 3282f21c97a0e1f66185923328d80d87fa5d8db7 $27.40 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/09 * eda59694400ba3aa79c7cb6ac131c643b119ad02 1b565047893eb8f55e839a9f0b5259d047547a82 $29.69 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/09 * 691ee60203f0e6df7344c4ffea35246d9fa22b96 1b565047893eb8f55e839a9f0b5259d047547a82 $17.11 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/10 * fc46581f9108f358044a454da70a26213d20abe0 1b565047893eb8f55e839a9f0b5259d047547a82 $70.04 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/10 * 691ee60203f0e6df7344c4ffea35246d9fa22b96 1b565047893eb8f55e839a9f0b5259d047547a82 $8.19 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/10 * 914ad295d9b70cdde0d6d7e20ea2da61c29327f1 1b565047893eb8f55e839a9f0b5259d047547a82 $5.75 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/11 * b0fd790e67ed296e4557dba02f51d2178ca15921 1b565047893eb8f55e839a9f0b5259d047547a82 $13.09 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/11 * 97a7a570c8ede9be3bb8ca6edb1ee14e1012c595 1b565047893eb8f55e839a9f0b5259d047547a82 $13.57 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/11 * cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $2.96 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/11 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df 1b565047893eb8f55e839a9f0b5259d047547a82 $3.05 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/11 * 7592910fc29b651a46c7c700406ed51978ae4cd6 3282f21c97a0e1f66185923328d80d87fa5d8db7 $24.30 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/11 * 6427432c39e8152357f8e82fd8399d118e199aa9 1b565047893eb8f55e839a9f0b5259d047547a82 $7.09 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/12 * e913379262b39496a6c00427697284cd786eb20d 1b565047893eb8f55e839a9f0b5259d047547a82 $7.87 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/13 2bb2072d9321de0d9b8030664c8a9333fdfa4a85 f2580c2fa4873496427487e068658993bbf70894 $5.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/08/13 * b6d03fa2a031c97515cf8e68ab32b80b8df4b658 1b565047893eb8f55e839a9f0b5259d047547a82 $8.88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/13 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $7.86 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/14 * 2470dca59588f4db61caa65758dc3be6e848d811 1b565047893eb8f55e839a9f0b5259d047547a82 $6.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/14 * 2ad323f9a17e92818c9ca3626175803d8e6eb1df 1b565047893eb8f55e839a9f0b5259d047547a82 $3.18 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-23.18 2004/08/15 * 0d9318bfa76160b6540a892d20f413e9a989b8ca f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $1,626.35 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/08/16 a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.74 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/16 bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $13.08 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/16 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $9.93 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/16 * 74c0fda1054b04bf3e2365d467e32a47e3feba7b fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $3.45 92a772d9a491a8c8f239d9148b979f1da7369480 2004/08/17 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $27.22 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/19 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $1.98 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/19 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $22.50 cfd76529eda7575c434ab6edd70e56693f979bb1 $20.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-42.50 2004/08/19 6311a5e415b6218c88c877cf57d459a79a646a22 92a772d9a491a8c8f239d9148b979f1da7369480 $7.04 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/19 612979bcaf308370105ac99e7dbbb8098f5b5b27 1b565047893eb8f55e839a9f0b5259d047547a82 $16.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/19 953388f315fdab9d0166aaa871ca84fc212af6fd 1b565047893eb8f55e839a9f0b5259d047547a82 $9.24 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/20 * f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $2.50 cfd76529eda7575c434ab6edd70e56693f979bb1 $40.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-42.50 2004/08/20 * 9b0b9af3274adf9514e492055259b4f9c17ce067 463628a20f371d71d46a7947f1175a0c16ce2f45 $950.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/08/21 * 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $29.17 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/08/21 * a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $79.46 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/08/21 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $14.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/08/21 d06547b2db2bfaa2e5e16a117f51635c8d851667 1b565047893eb8f55e839a9f0b5259d047547a82 $12.25 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/22 * dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $49.35 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/08/22 * 4ffc4349510313ea639fc1e50d7e156c456cdb84 1b565047893eb8f55e839a9f0b5259d047547a82 $67.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/08/24 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $6.95 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/24 685f23bc5eb93fc982292395e8c03a40670ce3ec eb0c1e3629fe7cba500081ef756a72e9659a93c4 $113.60 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/25 dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $49.41 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/08/25 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $100.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $2.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-102.00 2004/08/25 cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.05 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/25 973b27747b462069cd89ef63ac7fea9f431caca9 1b565047893eb8f55e839a9f0b5259d047547a82 $6.46 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/25 b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $32.94 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/27 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $25.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/27 a23f4dd42eb312db32096c3bb1766d31d4bb6c42 1b565047893eb8f55e839a9f0b5259d047547a82 $11.75 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/28 c0458f2551bfdbbaa65666d6cc9a4b918bc48520 f2580c2fa4873496427487e068658993bbf70894 $9.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/08/28 5a82e1f39afdb31637d2e54d696aac20e9449a6a c233d176ce06c06ecfd032230c4be5ff4476a554 $37.94 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/28 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $21.84 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/29 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $9.75 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/08/29 2470dca59588f4db61caa65758dc3be6e848d811 1b565047893eb8f55e839a9f0b5259d047547a82 $6.48 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/08/30 8c48fe5998e43553a8af87a1959965ff9e5f1c6e 1b565047893eb8f55e839a9f0b5259d047547a82 $6.71 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/30 4d565ed871c3d07f2878d73e18576ceb83f8700b 1b565047893eb8f55e839a9f0b5259d047547a82 $15.37 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/08/30 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $3.89 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/01 2c8ac236bcc8813497becd0e65a0aba085b05606 326b155986fe005914c2cd52851da075ff65992f $34.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/01 2c8ac236bcc8813497becd0e65a0aba085b05606 ab0ddaf550edf34ce2f7937aa3fb073d0240e8af $57.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/01 bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d 3282f21c97a0e1f66185923328d80d87fa5d8db7 $21.67 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/01 169151bd9488fb3e03ca31d580666e68aa2f20f0 1b565047893eb8f55e839a9f0b5259d047547a82 $17.48 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/01 c30acab5abcb7bb89524aa9f3daeeeb6de317c0e 1b565047893eb8f55e839a9f0b5259d047547a82 $18.19 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/02 046bb9cbb1f66bbca5bafa458fbe943c998a5552 1b565047893eb8f55e839a9f0b5259d047547a82 $24.32 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/03 03927f700749296a9e4ae0e7794cd87bf645274b cfd76529eda7575c434ab6edd70e56693f979bb1 $60.00 cecae7f2312046d2775a401cc3c3925b79676ce3 $3.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-63.00 2004/09/03 64c56683e1f14d64fe46bcf2763bcdc3db96e5a9 c233d176ce06c06ecfd032230c4be5ff4476a554 $17.15 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/09/03 10f29adc761ed3c5d5b4b7e4492919b75da151c8 1b565047893eb8f55e839a9f0b5259d047547a82 $7.51 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/04 cdc9806b703b6568d2e27bd0424c0e04938e2cff 1b565047893eb8f55e839a9f0b5259d047547a82 $16.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/04 50126a4484b34cea63f9724bea04d03cd3c2b611 1b565047893eb8f55e839a9f0b5259d047547a82 $8.35 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/05 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $23.43 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/06 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $4.88 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/07 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $9.36 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/07 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $19.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/08 612979bcaf308370105ac99e7dbbb8098f5b5b27 1b565047893eb8f55e839a9f0b5259d047547a82 $16.50 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/08 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $9.75 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/09 b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $30.70 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/10 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $19.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/10 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.59 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-53.59 2004/09/12 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $7.85 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/12 eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $8.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/12 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $12.81 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/13 3089591a3cd997b0823611f3ffe89b0f525ee88e 92a772d9a491a8c8f239d9148b979f1da7369480 $4.35 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/14 619cbfd21d72639f985324a9306696ce1262f3f6 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $3,000.00 7bd474a1c1d1afd2a0f22b563206deec4aba3e78 2004/09/15 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $2.97 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/15 77b9c5798a3049c3628dd7222459de0d251a4e8b 1b565047893eb8f55e839a9f0b5259d047547a82 $25.57 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/15 b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $23.47 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/15 4d565ed871c3d07f2878d73e18576ceb83f8700b 1b565047893eb8f55e839a9f0b5259d047547a82 $12.87 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/16 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $3.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/17 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $5.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/17 3c0a1c540b2499a98fc4f07ff7e8bc35a0c4921e 1b565047893eb8f55e839a9f0b5259d047547a82 $17.55 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/18 eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $8.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/18 c6408f67e3464256a4b56579f30647ac52a79ce0 1b565047893eb8f55e839a9f0b5259d047547a82 $18.77 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/19 5478fc25bca0ae185f2c2d36781fc1840ac0b42a f2580c2fa4873496427487e068658993bbf70894 $9.75 1b565047893eb8f55e839a9f0b5259d047547a82 $29.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/19 549c950857bf4f194f2d93cf98db69f9cdba2a8c 52e5ccdac27116d2919ae560eb4021c5addf9ca8 $0.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/21 66ed7e5615e2e59ea9ea2aa99a12243dbe0559c7 d27230e86aebbd6883e399ba2e38f635de9738a2 $17.21 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/09/21 a453f317c358a729d5a4e85bcca7e21c3054d184 9c484b5dc87055f93751ad00947fd9a7a14ea470 $79.56 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 2004/09/21 685f23bc5eb93fc982292395e8c03a40670ce3ec 92a772d9a491a8c8f239d9148b979f1da7369480 $8.65 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/21 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $28.34 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/22 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $13.91 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/23 0b209e14dc9dab896b962d3be3b5ab5808eac7cb 1b565047893eb8f55e839a9f0b5259d047547a82 $4.99 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/24 eb318e3f91d44bd7994f1be909b79e937415eced f2580c2fa4873496427487e068658993bbf70894 $8.00 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/24 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $21.65 cfd76529eda7575c434ab6edd70e56693f979bb1 $50.00 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $-71.65 2004/09/25 4df0c1da9898d6fc18ed9dbdd567ffbe3ec6f803 1b565047893eb8f55e839a9f0b5259d047547a82 $20.00 cfd76529eda7575c434ab6edd70e56693f979bb1 $15.00 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 $-35.00 2004/09/26 f753d77018be3e691ddf8c33796a33a7b36d39d8 1b565047893eb8f55e839a9f0b5259d047547a82 $23.87 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/26 b007369e15aba78cb6075310da96b854f5448a3a 3282f21c97a0e1f66185923328d80d87fa5d8db7 $26.67 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/26 f60050f870fc94a605d842605a2f366384de9962 f2580c2fa4873496427487e068658993bbf70894 $9.50 cfd76529eda7575c434ab6edd70e56693f979bb1 2004/09/26 80090f9d547f344402581e8a5dd6b5e71a342104 1b565047893eb8f55e839a9f0b5259d047547a82 $13.80 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/27 f6bc13e8a66d6bbdf8de6753aab036cc1c8db980 1b565047893eb8f55e839a9f0b5259d047547a82 $13.15 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/09/28 cb232a7a7077aa499f78ccdd5b4238a3ff1a4dcd 1b565047893eb8f55e839a9f0b5259d047547a82 $3.05 f0eb264dac24ed3a12eded5dfc3e3498e4ab13b9 2004/10/01 504bbaf175bfba4956ef5dc3a38e1ff693874597 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 $2,916.66 39189083b8637c7fff89e6bcf808790861417796 2004/09/21 dd68a4b38b001011fb067db32f136eb0a4790f95 9c484b5dc87055f93751ad00947fd9a7a14ea470 $44.28 fa9806a79e9cdf26d36d53646dd0aa2f70419c42 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/input/transfer.dat����������������������������������������������������������������0000664�0000000�0000000�00000014710�14411236400�0017413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2004/08/02 Transfer to hcoop.net Expenses:Internet 59820 bytes Liabilities:Payable:hcoop.net 2004/08/02 Transfer to hcoop.net Expenses:Internet 58626 bytes Liabilities:Payable:hcoop.net 2004/08/02 Transfer to hcoop.net Expenses:Internet 2997279 bytes Liabilities:Payable:hcoop.net 2004/08/03 Transfer to hcoop.net Expenses:Internet 227266 bytes Liabilities:Payable:hcoop.net 2004/08/03 Transfer to hcoop.net Expenses:Internet 54375 bytes Liabilities:Payable:hcoop.net 2004/08/05 Transfer to hcoop.net Expenses:Internet 54353 bytes Liabilities:Payable:hcoop.net 2004/08/07 Transfer to hcoop.net Expenses:Internet 54353 bytes Liabilities:Payable:hcoop.net 2004/08/16 Transfer to hcoop.net Expenses:Internet 54353 bytes Liabilities:Payable:hcoop.net 2004/08/20 Transfer to hcoop.net Expenses:Internet 133967 bytes Liabilities:Payable:hcoop.net 2004/08/20 Transfer to hcoop.net Expenses:Internet 124438 bytes Liabilities:Payable:hcoop.net 2004/08/21 Transfer to hcoop.net Expenses:Internet 148691 bytes Liabilities:Payable:hcoop.net 2004/08/22 Transfer to hcoop.net Expenses:Internet 660168 bytes Liabilities:Payable:hcoop.net 2004/08/22 Transfer to hcoop.net Expenses:Internet 2876287 bytes Liabilities:Payable:hcoop.net 2004/08/24 Transfer to hcoop.net Expenses:Internet 55166 bytes Liabilities:Payable:hcoop.net 2004/08/24 Transfer to hcoop.net Expenses:Internet 54743 bytes Liabilities:Payable:hcoop.net 2004/08/28 Transfer to hcoop.net Expenses:Internet 2414720 bytes Liabilities:Payable:hcoop.net 2004/08/30 Transfer to hcoop.net Expenses:Internet 396873 bytes Liabilities:Payable:hcoop.net 2004/08/30 Transfer to hcoop.net Expenses:Internet 2482301 bytes Liabilities:Payable:hcoop.net 2004/08/31 Transfer to hcoop.net Expenses:Internet 3696731 bytes Liabilities:Payable:hcoop.net 2004/09/01 Transfer to hcoop.net Expenses:Internet 2748364 bytes Liabilities:Payable:hcoop.net 2004/09/05 Transfer to hcoop.net Expenses:Internet 2500517 bytes Liabilities:Payable:hcoop.net 2004/09/06 Transfer to hcoop.net Expenses:Internet 54910 bytes Liabilities:Payable:hcoop.net 2004/09/06 Transfer to hcoop.net Expenses:Internet 9196055 bytes Liabilities:Payable:hcoop.net 2004/09/07 Transfer to hcoop.net Expenses:Internet 2640950 bytes Liabilities:Payable:hcoop.net 2004/09/07 Transfer to hcoop.net Expenses:Internet 94442 bytes Liabilities:Payable:hcoop.net 2004/09/08 Transfer to hcoop.net Expenses:Internet 2579158 bytes Liabilities:Payable:hcoop.net 2004/09/08 Transfer to hcoop.net Expenses:Internet 530904 bytes Liabilities:Payable:hcoop.net 2004/09/08 Transfer to hcoop.net Expenses:Internet 27368 bytes Liabilities:Payable:hcoop.net 2004/09/08 Transfer to hcoop.net Expenses:Internet 6593 bytes Liabilities:Payable:hcoop.net 2004/09/08 Transfer to hcoop.net Expenses:Internet 279 bytes Liabilities:Payable:hcoop.net 2004/09/08 Transfer to hcoop.net Expenses:Internet 773 bytes Liabilities:Payable:hcoop.net 2004/09/09 Transfer to hcoop.net Expenses:Internet 4217461 bytes Liabilities:Payable:hcoop.net 2004/09/10 Transfer to hcoop.net Expenses:Internet 100241 bytes Liabilities:Payable:hcoop.net 2004/09/10 Transfer to hcoop.net Expenses:Internet 117866 bytes Liabilities:Payable:hcoop.net 2004/09/10 Transfer to hcoop.net Expenses:Internet 111275 bytes Liabilities:Payable:hcoop.net 2004/09/11 Transfer to hcoop.net Expenses:Internet 2717020 bytes Liabilities:Payable:hcoop.net 2004/09/12 Transfer to hcoop.net Expenses:Internet 88188 bytes Liabilities:Payable:hcoop.net 2004/09/13 Transfer to hcoop.net Expenses:Internet 3046553 bytes Liabilities:Payable:hcoop.net 2004/09/13 Transfer to hcoop.net Expenses:Internet 3370971 bytes Liabilities:Payable:hcoop.net 2004/09/13 Transfer to hcoop.net Expenses:Internet 2941407 bytes Liabilities:Payable:hcoop.net 2004/09/13 Transfer to hcoop.net Expenses:Internet 2101943 bytes Liabilities:Payable:hcoop.net 2004/09/15 Transfer to hcoop.net Expenses:Internet 3050131 bytes Liabilities:Payable:hcoop.net 2004/09/15 Transfer to hcoop.net Expenses:Internet 2638687 bytes Liabilities:Payable:hcoop.net 2004/09/15 Transfer to hcoop.net Expenses:Internet 2668178 bytes Liabilities:Payable:hcoop.net 2004/09/16 Transfer to hcoop.net Expenses:Internet 2645212 bytes Liabilities:Payable:hcoop.net 2004/09/17 Transfer to hcoop.net Expenses:Internet 336579 bytes Liabilities:Payable:hcoop.net 2004/09/17 Transfer to hcoop.net Expenses:Internet 104305 bytes Liabilities:Payable:hcoop.net 2004/09/18 Transfer to hcoop.net Expenses:Internet 3117533 bytes Liabilities:Payable:hcoop.net 2004/09/19 Transfer to hcoop.net Expenses:Internet 120777 bytes Liabilities:Payable:hcoop.net 2004/09/20 Transfer to hcoop.net Expenses:Internet 193151 bytes Liabilities:Payable:hcoop.net 2004/09/20 Transfer to hcoop.net Expenses:Internet 56204 bytes Liabilities:Payable:hcoop.net 2004/09/21 Transfer to hcoop.net Expenses:Internet 315126 bytes Liabilities:Payable:hcoop.net 2004/09/22 Transfer to hcoop.net Expenses:Internet 138076 bytes Liabilities:Payable:hcoop.net 2004/09/24 Transfer to hcoop.net Expenses:Internet 817190 bytes Liabilities:Payable:hcoop.net 2004/10/05 Transfer to hcoop.net Expenses:Internet 356104 bytes Liabilities:Payable:hcoop.net ��������������������������������������������������������ledger-3.3.2/test/input/wow.dat���������������������������������������������������������������������0000664�0000000�0000000�00000035744�14411236400�0016415�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������C 1.00s = 100c C 1.00G = 100s D 1.00G 2006/03/14 Opening Balances Assets:Tajer 1339829c Assets:Gruulmorg 248720c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1428c Expenses:Fees:Auction 768c Expenses:Fees:Auction 612c Expenses:Fees:Auction 4764c Expenses:Fees:Auction 3372c Expenses:Fees:Auction 1296c Expenses:Fees:Auction 1332c Expenses:Fees:Auction 660c Expenses:Fees:Auction 10044c Expenses:Fees:Auction 3588c Expenses:Fees:Auction 1632c Expenses:Fees:Auction 8388c Expenses:Fees:Auction 9984c Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 158860c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 1320c Assets:Tajer 2006/03/14 Auction House Assets:Tajer 11496c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 3216c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/14 Auction House Assets:Tajer 34678c Equity:Gold 2006/03/14 Auction House Expenses:Fees:Auction 2316c Assets:Tajer 2006/03/14 Auction House Assets:Gruulmorg 1G Equity:Gold 2006/03/14 Auction House Assets:Tajer 59389c Equity:Gold 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Player Assets:Tajer 3G Equity:Gold 2006/03/14 Player Assets:Tajer 6G Equity:Gold 2006/03/14 Auction House Expenses:Items 35s Assets:Tajer 2006/03/14 Auction House Assets:Tajer:Items "Plans: Wildthorn Mail" 1 @ 125s Assets:Tajer 2006/03/14 Auction House Assets:Bids 259c Assets:Bids 45s Assets:Bids 4720c Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Auction House Expenses:Items 8G Assets:Tajer 2006/03/14 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/14 Puldoost Assets:Tajer 8G Expenses:Items 2006/03/14 Auction House Assets:Wyshona:Items "Plans: Wildthorn Mail" 1 {1.25G} Assets:Tajer:Items 2006/03/15 Auction House Assets:Tajer 45s Assets:Tajer 259c Assets:Bids 2006/03/15 Auction House Assets:Tajer 4720c Assets:Bids 2006/03/15 Auction House Expenses:Fees:Auction 12542c ; something got lost here Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 2375c Expenses:Fees:Auction -2375c Assets:Gruulmorg 2006/03/15 Auction House Assets:Danell 4c Equity:Gold 2006/03/15 Transfer Assets:Gruulmorg 2c Assets:Danell 2006/03/15 Transfer Assets:Danell 4250c Expenses:Fees:Auction 750c Assets:Gruulmorg -50s 2006/03/15 Post Expenses:Fees:Mail 60c Assets:Danell 2006/03/15 Player Assets:Tajer 3G Equity:Gold 2006/03/15 Post Assets:Wyshona 40s Expenses:Fees:Mail 30c Assets:Danell 2006/03/15 Auction House Expenses:Fees:Auction 8s Expenses:Fees:Auction 11s Assets:Wyshona 2006/03/15 Auction House Assets:Tajer:Items "Beaststalker's Belt" 1 @ 65G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Vendor Assets:Tajer 16744c Assets:Tajer 16640c Equity:Gold 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 772c Expenses:Fees:Auction 544c Expenses:Fees:Auction 444c Expenses:Fees:Auction 432c Expenses:Fees:Auction 204c Assets:Tajer 2006/03/15 Player Assets:Tajer 12s Equity:Gold 2006/03/15 Vendor Assets:Tajer 22s Equity:Gold 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 1G Assets:Tajer 2006/03/15 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/15 Auction House Expenses:Fees:Auction 8268c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 21050c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Plans: Mithril Shield Spike" 1 @ 23000c Assets:Tajer 2006/03/15 Auction House Assets:Tajer:Items "Recipe: Elixir of Giant Growth" 1 @ 150s Assets:Tajer 2006/03/16 Player Assets:Tajer 3G Equity:Gold 2006/03/16 Post Expenses:Fees:Mail 90c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 1195768c Assets:Tajer:Items "Beaststalker's Belt" -1 {65G} @ 1195768c Income:Brokering -545768c 2006/03/16 Auction House Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {21050c} Assets:Wyshona:Items "Plans: Mithril Shield Spike" 1 {2.3G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1G} Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" 1 {1.5G} Assets:Tajer:Items 2006/03/16 Player Assets:Tajer 4G Equity:Gold 2006/03/16 Auction House Assets:Wyshona 1341s Equity:Gold 2006/03/16 Auction House Assets:Gruulmorg 4c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 4c Expenses:Fees:Mail 120c Assets:Danell 2006/03/16 Auction House Expenses:Fees:Auction 24s Expenses:Fees:Auction 12s Expenses:Fees:Auction 12s Expenses:Fees:Auction 84c Expenses:Fees:Auction 84c Assets:Wyshona 2006/03/16 Crazy Cat Lady Expenses:Items 40s Expenses:Items 40s Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 60c Assets:Danell 5G Assets:Wyshona 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Tajer 20G Assets:Gruulmorg 2006/03/16 Auction House Assets:Tajer:Items "Pulsating Hydra Heart" 1 @ 1G Assets:Tajer 2006/03/16 Auction House Expenses:Fees:Auction 936c Assets:Tajer 2006/03/16 Transfer Expenses:Fees:Mail 30c Assets:Gruulmorg 30G Assets:Tajer 2006/03/16 Auction House Assets:Gruulmorg:Items "Ace of Warlords" 2 @ 15G Assets:Gruulmorg 2006/03/16 Transfer Assets:Tajer:Items "Ace of Warlords" 2 {15G} Assets:Gruulmorg:Items 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 120c Assets:Tajer 2006/03/16 Auction House Assets:Tajer 2104c Equity:Gold 2006/03/16 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/16 Transfer Assets:Danell 6c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Gruulmorg 2006/03/16 Post Expenses:Fees:Mail 60c Assets:Tajer 2006/03/16 General Goods Vendor Expenses:Items 50c ; wrapping paper Assets:Tajer 2006/03/16 Player Assets:Tajer 1G Equity:Gold 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1.5G} @ 180584c Income:Brokering -165584c 2006/03/17 Auction House Assets:Wyshona 180584c Assets:Wyshona:Items "Recipe: Elixir of Giant Growth" -1 {1G} @ 180584c Income:Brokering -170584c 2006/03/17 Post Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Player: raev Assets:Tajer:Items "Wildheart Belt" 1 {30G} Assets:Tajer:Items "Ace of Warlords" -2 {15G} 2006/03/17 Auction House Expenses:Fees:Auction 7482c Assets:Tajer 2006/03/17 Post Expenses:Fees:Mail 300c Assets:Wyshona 2006/03/17 Player Assets:Wyshona 1G Assets:Wyshona:Items "Plans: Wildthorn Mail" -1 {1.25G} @ 1G Expenses:Capital Loss 25s 2006/03/17 Auction House (implicit transfer) Expenses:Items 279s ; Recipe: Swiftness Potion Assets:Wyshona 2006/03/17 Auction House (implicit transfer) Assets:Danell:Items "Ace of Warlords" 1 @ 3G Assets:Tajer:Items "Ace of Warlords" 1 @ 3.9G Assets:Tajer:Items "Holy Bologna" 1 @ 2G Assets:Tajer:Items "The Emerald Dream" 1 @ 4G Assets:Tajer:Items "The Arcanist's Cookbook" 1 @ 4G Assets:Tajer:Items "Harnessing Shadows" 1 @ 5G Assets:Tajer:Items "Garona: Book on Treachery" 1 @ 4G Assets:Tajer:Items "Preserved Holly" 5 @ 20s Assets:Wyshona 2006/03/17 Auction House Assets:Tajer 4G Assets:Tajer:Items "Pulsating Hydra Heart" -1 {1G} @ 4G Income:Brokering -3G 2006/03/17 Auction House Assets:Danell 3171c Assets:Danell:Items "Ace of Warlords" -1 {3G} @ 3171c Expenses:Capital Loss 26829c 2006/03/17 Auction House Expenses:Fees:Auction 12537c Assets:Danell 2006/03/17 Transfer Assets:Gruulmorg 15G Expenses:Fees:Mail 30c Assets:Tajer 2006/03/17 Auction House Assets:Wyshona 362450c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {21050c} @ 181225c Assets:Wyshona:Items "Plans: Mithril Shield Spike" -1 {2.3G} @ 181225c Income:Brokering -318400c 2006/03/17 Transfer Assets:Danell 499560c Expenses:Gifts 1G Expenses:Fees:Mail 30c Assets:Wyshona 2006/03/17 Post Expenses:Fees:Mail 90c Expenses:Fees:Auction 166c Assets:Gruulmorg 2006/03/17 Transfer Assets:Gruulmorg 459211c Expenses:Fees:Auction 81023c Assets:Danell -540234c 2006/03/17 Transfer Assets:Tajer 662465c Expenses:Fees:Mail 30c Assets:Gruulmorg 2006/03/17 Auction House Expenses:Fees:Auction 75s Expenses:Fees:Mail 30c Assets:Tajer 2006/03/18 Auction House Assets:Tajer 15G Assets:Tajer:Items "Ace of Warlords" -1 {3.9G} @ 15G Income:Brokering -111000c 2006/03/18 Auction House Assets:Tajer 434472c Assets:Tajer:Items "Wildheart Belt" -1 {30G} @ 434472c Income:Brokering -134472c 2006/03/18 Auction House Assets:Tajer 1995s Assets:Tajer:Items "Harnessing Shadows" -1 {5G} @ 1995s Income:Brokering -1495s 2006/03/19 Auction House Assets:Tajer 2850s Assets:Tajer:Items "Garona: Book on Treachery" -1 {4G} @ 2850s Income:Brokering -2450s 2006/03/19 Auction House Assets:Tajer 1710s Assets:Tajer:Items "The Arcanist's Cookbook" -1 {4G} @ 1710s Income:Brokering -1310s 2006/03/19 Auction House Assets:Tajer 46550c Assets:Tajer:Items "Preserved Holly" -5 {20s} @ 9310c Income:Brokering -36550c 2006/03/19 Auction House Assets:Tajer:Items "Two of Portals" 1 @ 3G Assets:Tajer:Items "Two of Portals" 1 @ 2.5G Assets:Tajer 2006/03/20 Auction House Assets:Tajer 163443c Assets:Tajer:Items "Holy Bologna" -1 {2G} @ 163443c Income:Brokering -143443c 2006/03/20 Auction House Assets:Tajer 5G Assets:Tajer:Items "Two of Portals" -1 {3G} @ 5G Income:Brokering -2G 2006/03/20 Auction House Assets:Tajer 15G Assets:Tajer:Items "The Emerald Dream" -1 {4G} @ 15G Income:Brokering -11G 2006/03/20 Auction House Expenses:Fees:Mail 60c Assets:Tajer 2006/03/21 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 170G Assets:Tajer 2006/03/21 Auction House Expenses:Fees:Auction 2760c Expenses:Fees:Auction 2760c Assets:Tajer 2006/03/22 Auction House Assets:Tajer:Items "Nightblade" 1 @ 200G Assets:Tajer 2006/03/22 Auction House Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 177s Expenses:Fees:Auction 75s Assets:Tajer 2006/03/23 Auction House Assets:Tajer 1665260c Assets:Tajer:Items "Orb of Deception" -1 {170G} @ 1665260c Expenses:Capital Loss 34740c 2006/03/26 Auction House Assets:Tajer 81980c Assets:Tajer:Items "Two of Portals" -1 {2.5G} @ 81980c Income:Brokering -56980c 2006/03/26 Player Expenses:Items 150s ; Recipe: Elixir of Minor Agility Expenses:Fees:Mail 30c Expenses:Fees:Mail 30c Assets:Tajer 2006/03/27 Player Assets:Tajer 160G Assets:Tajer:Items "Nightblade" -1 {200G} @ 160G Expenses:Capital Loss 40G 2006/03/27 Player Expenses:Fees:Mail 30c Assets:Tajer 2006/03/26 Player Expenses:Items (9G * 6) ; Traveler's backpacks Expenses:Items 10G Expenses:Fees:Bank 10G Expenses:Fees:Mail 630c Expenses:Fees:Mail 330c Expenses:Fees:Mail 30c Assets:Tajer 2006/04/01 Auction House Assets:Tajer:Items "Orb of Deception" 1 @ 155G Assets:Tajer ����������������������������ledger-3.3.2/test/manual/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0015210�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/CMakeLists.txt�������������������������������������������������������������0000664�0000000�0000000�00000000041�14411236400�0017743�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������add_ledger_harness_tests(Manual) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-codes-1.test���������������������������������������������������0000664�0000000�0000000�00000001032�14411236400�0021663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/10/29 (XFER) Panera Bread Expenses:Food $4.50 Assets:Checking 2009/10/30 (DEP) Pay day! Assets:Checking $20.00 Income 2009/10/30 (XFER) Panera Bread Expenses:Food $4.50 Assets:Checking 2009/10/31 (559385768438A8D7) Panera Bread Expenses:Food $4.50 Liabilities:Credit Card test reg --columns=60 food and code xfer 09-Oct-29 Panera Bread Expenses:Food $4.50 $4.50 09-Oct-30 Panera Bread Expenses:Food $4.50 $9.00 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-codes-2.test���������������������������������������������������0000664�0000000�0000000�00000001027�14411236400�0021670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/10/29 (XFER) Panera Bread Expenses:Food $4.50 Assets:Checking 2009/10/30 (DEP) Pay day! Assets:Checking $20.00 Income 2009/10/30 (XFER) Panera Bread Expenses:Food $4.50 Assets:Checking 2009/10/31 (559385768438A8D7) Panera Bread Expenses:Food $4.50 Liabilities:Credit Card test bal checking --account=code $20.00 DEP:Assets:Checking $-9.00 XFER:Assets:Checking -------------------- $11.00 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-notes-1.test���������������������������������������������������0000664�0000000�0000000�00000001242�14411236400�0021721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/11/01 Panera Bread ; Got something to eat Expenses:Food $4.50 Assets:Checking 2009/11/01 Panera Bread ; Type: Coffee ; Let’s see, I ate a whole bunch of stuff, drank some coffee, ; pondered a bagel, then decided against the donut. Expenses:Food $4.50 Assets:Checking 2009/11/01 Panera Bread ; Type: Dining ; :Eating: ; This is another long note, after the metadata. Expenses:Food $4.50 Assets:Checking test reg --columns=60 food and note eat 09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 09-Nov-01 Panera Bread Expenses:Food $4.50 $9.00 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-notes-2.test���������������������������������������������������0000664�0000000�0000000�00000001147�14411236400�0021726�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/11/01 Panera Bread ; Got something to eat Expenses:Food $4.50 Assets:Checking 2009/11/01 Panera Bread ; Type: Coffee ; Let’s see, I ate a whole bunch of stuff, drank some coffee, ; pondered a bagel, then decided against the donut. Expenses:Food $4.50 Assets:Checking 2009/11/01 Panera Bread ; Type: Dining ; :Eating: ; This is another long note, after the metadata. Expenses:Food $4.50 Assets:Checking test reg --columns=60 food and tag eating 09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-notes-3.test���������������������������������������������������0000664�0000000�0000000�00000001154�14411236400�0021725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/11/01 Panera Bread ; Got something to eat Expenses:Food $4.50 Assets:Checking 2009/11/01 Panera Bread ; Type: Coffee ; Let’s see, I ate a whole bunch of stuff, drank some coffee, ; pondered a bagel, then decided against the donut. Expenses:Food $4.50 Assets:Checking 2009/11/01 Panera Bread ; Type: Dining ; :Eating: ; This is another long note, after the metadata. Expenses:Food $4.50 Assets:Checking test reg --columns=60 food and tag type=dining 09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-notes-4.test���������������������������������������������������0000664�0000000�0000000�00000001334�14411236400�0021726�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/11/01 Panera Bread ; Got something to eat Expenses:Food $4.50 Assets:Checking 2009/11/01 Panera Bread ; Type: Coffee ; Let’s see, I ate a whole bunch of stuff, drank some coffee, ; pondered a bagel, then decided against the donut. Expenses:Food $4.50 Assets:Checking 2009/11/01 Panera Bread ; Type: Dining ; :Eating: ; This is another long note, after the metadata. Expenses:Food $4.50 Assets:Checking test bal food and tag type --account='"Tags:" + tag("Type")' $9.00 Tags $4.50 Coffee:Expenses:Food $4.50 Dining:Expenses:Food -------------------- $9.00 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-status-1.test��������������������������������������������������0000664�0000000�0000000�00000000675�14411236400�0022125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/10/31 * Panera Bread Expenses:Food $4.50 Assets 2009/11/01 ! Panera Bread Expenses:Food $4.50 Assets 2009/11/02 Panera Bread Expenses:Food $4.50 Assets test reg --columns=60 food 09-Oct-31 Panera Bread Expenses:Food $4.50 $4.50 09-Nov-01 Panera Bread Expenses:Food $4.50 $9.00 09-Nov-02 Panera Bread Expenses:Food $4.50 $13.50 end test �������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-status-2.test��������������������������������������������������0000664�0000000�0000000�00000000515�14411236400�0022117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/10/31 * Panera Bread Expenses:Food $4.50 Assets 2009/11/01 ! Panera Bread Expenses:Food $4.50 Assets 2009/11/02 Panera Bread Expenses:Food $4.50 Assets test reg --columns=60 food --cleared 09-Oct-31 Panera Bread Expenses:Food $4.50 $4.50 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-status-3.test��������������������������������������������������0000664�0000000�0000000�00000000614�14411236400�0022120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/10/31 * Panera Bread Expenses:Food $4.50 Assets 2009/11/01 ! Panera Bread Expenses:Food $4.50 Assets 2009/11/02 Panera Bread Expenses:Food $4.50 Assets test reg --columns=60 food --uncleared 09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 09-Nov-02 Panera Bread Expenses:Food $4.50 $9.00 end test ��������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/manual/transaction-status-4.test��������������������������������������������������0000664�0000000�0000000�00000000515�14411236400�0022121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/10/31 * Panera Bread Expenses:Food $4.50 Assets 2009/11/01 ! Panera Bread Expenses:Food $4.50 Assets 2009/11/02 Panera Bread Expenses:Food $4.50 Assets test reg --columns=60 food --pending 09-Nov-01 Panera Bread Expenses:Food $4.50 $4.50 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/python/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0015254�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/python/JournalTest.py�������������������������������������������������������������0000664�0000000�0000000�00000002432�14411236400�0020101�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- import unittest from ledger import * class JournalTestCase(unittest.TestCase): def tearDown(self): close_journal_files() def testBasicRead(self): journal = read_journal_from_string(""" 2012-03-01 KFC Expenses:Food $21.34 Assets:Cash """) self.assertEqual(type(journal), Journal) for xact in journal: self.assertEqual(xact.payee, "KFC") for post in journal.query("food"): self.assertEqual(str(post.account), "Expenses:Food") self.assertEqual(post.amount, Amount("$21.34")) def testParseError(self): # TODO: ledger spits out parse errors to standard out. # This should not happen, especially when the error # has already been captured by a Python exception. def fun(): read_journal_from_string(""" 2012-03-01 KFC Expenses:Food rsnetnirsnti Assets:Cash """) self.assertRaises(RuntimeError, fun) try: fun() except RuntimeError as e: self.assertEqual(str(e).splitlines()[-1], "No quantity specified for amount") def suite(): return unittest.TestLoader().loadTestsFromTestCase(JournalTestCase) if __name__ == '__main__': unittest.main() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/python/PostingTest.py�������������������������������������������������������������0000664�0000000�0000000�00000000624�14411236400�0020113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- import unittest import operator from ledger import * from datetime import * class PostingTestCase(unittest.TestCase): def setUp(self): pass def tearDown(self): close_journal_files() def test_(self): pass def suite(): return unittest.TestLoader().loadTestsFromTestCase(PostingTestCase) if __name__ == '__main__': unittest.main() ������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/python/TransactionTest.py���������������������������������������������������������0000664�0000000�0000000�00000002465�14411236400�0020762�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- import unittest import operator from ledger import * from datetime import * class TransactionTestCase(unittest.TestCase): def setUp(self): self.journal = read_journal_from_string(""" 2012-03-01 KFC ;this is a note Expenses:Food $21.34 Assets:Cash 2012-03-02 MJT Expenses:Museum $45.67 Assets:Cash """) def tearDown(self): close_journal_files() def testAddRemovePosts(self): xacts = [xact for xact in self.journal] x1_posts = [post for post in xacts[1]] for post in x1_posts: xacts[0].add_post(post) xacts[1].remove_post(post) x0_posts = [post for post in xacts[0]] x1_posts = [post for post in xacts[1]] self.assertEqual(len(x0_posts), 4) self.assertEqual(len(x1_posts), 0) def testSetNote(self): xacts = [xact for xact in self.journal] self.assertEqual(xacts[0].note, 'this is a note') xacts[0].note = 'this is also a note' self.assertEqual(xacts[0].note, 'this is also a note') xacts[0].note += 'so is this' self.assertEqual(xacts[0].note, 'this is also a noteso is this') def suite(): return unittest.TestLoader().loadTestsFromTestCase(TransactionTestCase) if __name__ == '__main__': unittest.main() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/python/UnitTests.py���������������������������������������������������������������0000664�0000000�0000000�00000000361�14411236400�0017570�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from unittest import TextTestRunner, TestSuite import JournalTest import TransactionTest import PostingTest suites = [ JournalTest.suite(), TransactionTest.suite(), PostingTest.suite() ] TextTestRunner().run(TestSuite(suites)) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/��������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0015405�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/012ADB60.test�������������������������������������������������������������0000664�0000000�0000000�00000001341�14411236400�0017264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2005/01/03 * Pay Credit card Liabilities:CredCard $1,000.00 ; Electronic/ACH Debit Assets:Current:Checking ; Electronic/ACH Debit (Virtualaccount) $1,000.00 2006/01/03 Gift shop Expenses:Gifts $46.50 * Liabilities:CredCard 2006/01/03 Bike shop Expenses:Misc $199.00 * Liabilities:CredCard (testvirtual) $184.72 2006/01/04 Store Expenses:Misc $49.95 * Liabilities:CredCard test equity -e 2006 2005/01/03 Opening Balances Assets:Current:Checking $-1,000.00 Liabilities:CredCard $1,000.00 (Virtualaccount) $1,000.00 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/0161EB1E.test�������������������������������������������������������������0000664�0000000�0000000�00000001013�14411236400�0017265�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������bucket Assets:Checking 2011/04/25 Tom's Used Cars Auto $ 5,500.00 ; :nobudget: A Assets:Checking 2011/04/27 Book Store Books $20.00 test reg 11-Apr-25 Tom's Used Cars Auto $ 5,500.00 $ 5,500.00 Assets:Checking $ -5,500.00 0 11-Apr-27 Book Store Books $ 20.00 $ 20.00 Assets:Checking $ -20.00 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/04C5E1CA.test�������������������������������������������������������������0000664�0000000�0000000�00000001316�14411236400�0017314�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/04/04 CS Club Sign Expenses:School:CS Club:Home Depot:4" Brush 2 @ $3.97 Liabilities:Mastercard 2009/04/04 CS Club Sign Expenses:School:CS Club:Home Depot:4" Brush (2 * $3.97) Liabilities:Mastercard test reg 09-Apr-04 CS Club Sign Ex:Sc:CS:Home:4" Brush 2 2 Liabilities:Mastercard $-7.94 2 $-7.94 09-Apr-04 CS Club Sign Ex:Sc:CS:Home:4" Brush $7.94 2 Liabilities:Mastercard $-7.94 2 $-7.94 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/04D86CD0.test�������������������������������������������������������������0000664�0000000�0000000�00000000231�14411236400�0017276�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ; Test for invalid option embedded in journal --foo test bal -> 1 __ERROR__ While parsing file "$FILE", line 4: Error: Illegal option --foo end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/0CA014F9.test�������������������������������������������������������������0000664�0000000�0000000�00000000501�14411236400�0017271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/02/02 RD VMMXX Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 test bal 0.350 VMMXX Assets:Investments:Vanguard:VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX -------------------- $-0.35 0.350 VMMXX end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/0DDDEBC0.dat��������������������������������������������������������������0000664�0000000�0000000�00000000100�14411236400�0017133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,posted,amount, 12/12/2011,12/13/2011,$10, 12/12/2011,,$20, ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/0DDDEBC0.test�������������������������������������������������������������0000664�0000000�0000000�00000000424�14411236400�0017353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test -f /dev/null --input-date-format '%m/%d/%Y' convert test/regress/0DDDEBC0.dat 2011/12/12=2011/12/13 * Expenses:Unknown $10 Equity:Unknown 2011/12/12 * Expenses:Unknown $20 Equity:Unknown end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1036.test�����������������������������������������������������������������0000664�0000000�0000000�00000003142�14411236400�0016677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ account alias apply assert bucket check commodity def define apply account foo end expr eval include !include import payee tag comment foo bar end comment value test source -> 17 __ERROR__ While parsing file "$FILE", line 2: Error: Directive 'account' requires an argument While parsing file "$FILE", line 4: Error: Directive 'alias' requires an argument While parsing file "$FILE", line 6: Error: Directive 'apply' requires an argument While parsing file "$FILE", line 8: Error: Directive 'assert' requires an argument While parsing file "$FILE", line 10: Error: Directive 'bucket' requires an argument While parsing file "$FILE", line 12: Error: Directive 'check' requires an argument While parsing file "$FILE", line 14: Error: Directive 'commodity' requires an argument While parsing file "$FILE", line 16: Error: Directive 'def' requires an argument While parsing file "$FILE", line 18: Error: Directive 'define' requires an argument While parsing file "$FILE", line 23: Error: Directive 'expr' requires an argument While parsing file "$FILE", line 25: Error: Directive 'eval' requires an argument While parsing file "$FILE", line 27: Error: Directive 'include' requires an argument While parsing file "$FILE", line 29: Error: Directive 'include' requires an argument While parsing file "$FILE", line 31: Error: Directive 'import' requires an argument While parsing file "$FILE", line 33: Error: Directive 'payee' requires an argument While parsing file "$FILE", line 35: Error: Directive 'tag' requires an argument While parsing file "$FILE", line 41: Error: Directive 'value' requires an argument end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1038_1.test���������������������������������������������������������������0000664�0000000�0000000�00000000635�14411236400�0017125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Y2014 04/13 Bank Expenses:Loan $400 Assets:Cash 05/13 Bug 1038 Test Expenses:Some:Account $500 Assets:Cash 06/13 Landlord Expenses:Rent $600 Assets:Cash test reg --now 2014-05-14 -p 'this month' 14-May-13 Bug 1038 Test Expenses:Some:Account $500 $500 Assets:Cash $-500 0 end test ���������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1038_2.test���������������������������������������������������������������0000664�0000000�0000000�00000000641�14411236400�0017123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������year 2014 04/13 Bank Expenses:Loan $400 Assets:Cash 05/13 Bug 1038 Test Expenses:Some:Account $500 Assets:Cash 06/13 Landlord Expenses:Rent $600 Assets:Cash test reg --now 2014-05-14 -p 'this month' 14-May-13 Bug 1038 Test Expenses:Some:Account $500 $500 Assets:Cash $-500 0 end test �����������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1038_3.test���������������������������������������������������������������0000664�0000000�0000000�00000000662�14411236400�0017127�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������apply year 2014 04/13 Bank Expenses:Loan $400 Assets:Cash 05/13 Bug 1038 Test Expenses:Some:Account $500 Assets:Cash 06/13 Landlord Expenses:Rent $600 Assets:Cash end apply test reg --now 2014-05-14 -p 'this month' 14-May-13 Bug 1038 Test Expenses:Some:Account $500 $500 Assets:Cash $-500 0 end test ������������������������������������������������������������������������������ledger-3.3.2/test/regress/1046.test�����������������������������������������������������������������0000664�0000000�0000000�00000001711�14411236400�0016700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2014-05-01 * Test 1 A 1.00 GBP (@) 1.23 EUR B -1.23 EUR 2014-05-02 * Test 2 A 1.00 GBP (@@) 1.23 EUR B -1.23 EUR 2014-05-03 * Test 3 A 1.00 GBP @ 1.23 EUR B -1.23 EUR 2014-05-04 * Test 4 A 1.00 GBP @@ 1.23 EUR B -1.23 EUR test print 2014/05/01 * Test 1 A 1.00 GBP (@) 1.23 EUR B -1.23 EUR 2014/05/02 * Test 2 A 1.00 GBP (@@) 1.23 EUR B -1.23 EUR 2014/05/03 * Test 3 A 1.00 GBP @ 1.23 EUR B -1.23 EUR 2014/05/04 * Test 4 A 1.00 GBP @@ 1.23 EUR B -1.23 EUR end test �������������������������������������������������������ledger-3.3.2/test/regress/1050.test�����������������������������������������������������������������0000664�0000000�0000000�00000001103�14411236400�0016666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ D $1000.00 D £1000.00 D €1000.00 D 1000.00 EUR 2014-06-05 * Test A 2 EUR @$1.37 C 2014-06-05 * Test A 2 EUR @@£1.62 C 2014-06-05 * Test A 2 EUR (@)€1.00 C 2014-06-05 * Test A 2 EUR (@@)€2.00 C test bal 8.00 EUR A $-2.74 £-1.62 €-4.00 C -------------------- $-2.74 8.00 EUR £-1.62 €-4.00 end test test pricedb P 2014/06/05 00:00:00 EUR $1.37 P 2014/06/05 00:00:00 EUR £0.81 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1055.test�����������������������������������������������������������������0000664�0000000�0000000�00000001516�14411236400�0016703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011-01-01 * Opening balance Assets:Broker 250.00 GBP = 250.00 GBP Equity:Opening balance -250.00 GBP 2011-02-01 * Buy 1 AAA for 10.00 GBP Assets:Broker 1 AAA = 1 AAA Assets:Broker -10.00 GBP 2011-03-01 * Buy 1 AAA for 10.00 GBP Assets:Broker 1 AAA = 2 AAA Assets:Broker -10.00 GBP 2011-04-01 * Buy 1 BBB for 15.00 GBP Assets:Broker 1 BBB = 1 BBB Assets:Broker -15.00 GBP test bal 2 AAA 1 BBB 215.00 GBP Assets:Broker -250.00 GBP Equity:Opening balance -------------------- 2 AAA 1 BBB -35.00 GBP end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1057.test�����������������������������������������������������������������0000664�0000000�0000000�00000001014�14411236400�0016676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2014/04/03 www.amazon.fr Dépense:Loisir:Ordi:Matériel 101,50 € ; disque dur portable 2,5" 2000 Go Dépense:Maison:Service:Poste * Passif:Crédit:BanqueAccord -171,63 € test --now=2014/06/27 emacs (("$sourcepath/test/regress/1057.test" 1 (21308 60112 0) nil "www.amazon.fr" (2 "Dépense:Loisir:Ordi:Matériel" "101,50 €" nil " disque dur portable 2,5\" 2000 Go") (3 "Dépense:Maison:Service:Poste" "70,13 €" nil) (4 "Passif:Crédit:BanqueAccord" "-171,63 €" t))) end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1072.test�����������������������������������������������������������������0000664�0000000�0000000�00000002142�14411236400�0016676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ --input-date-format %d/%m/%y --date-format %d/%m/%y 1/1/14 * Test A $10 B 12/1/14 * Test A $20 B test --input-date-format %d/%m/%y reg --begin 2/1/13 01/01/14 Test A $10 $10 B $-10 0 12/01/14 Test A $20 $20 B $-20 0 end test test --input-date-format %d/%m/%y reg --begin 1/1/14 01/01/14 Test A $10 $10 B $-10 0 12/01/14 Test A $20 $20 B $-20 0 end test test --input-date-format %d/%m/%y reg --begin 2/1/14 12/01/14 Test A $20 $20 B $-20 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1074.test�����������������������������������������������������������������0000664�0000000�0000000�00000015152�14411236400�0016705�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ --input-date-format %Y-%m-%d --date-format %Y-%m-%d 2011-06-01 * Jun 2011 A $10 B 2011-07-01 * Jul 2011 A $10 B 2011-08-01 * Aug 2011 A $10 B 2012-06-01 * Jun 2012 A $10 B 2012-07-01 * Jul 2012 A $10 B 2012-08-01 * Aug 2012 A $10 B 2013-06-01 * Jun 2013 A $10 B 2013-07-01 * Jul 2013 A $10 B 2013-08-01 * Aug 2013 A $10 B 2014-06-01 * Jun 2014 A $10 B 2014-07-01 * Jul 2014 A $10 B 2014-08-01 * Aug 2014 A $10 B 2015-06-01 * Jun 2015 A $10 B 2015-07-01 * Jul 2015 A $10 B 2015-08-01 * Aug 2015 A $10 B test --now 2012-02-03 reg -p "from june to july" 2012-06-01 Jun 2012 A $10 $10 B $-10 0 end test test --now 2013-02-03 reg -p "from june to july" 2013-06-01 Jun 2013 A $10 $10 B $-10 0 end test test --now 2014-02-03 reg -p "from june to july" 2014-06-01 Jun 2014 A $10 $10 B $-10 0 end test test --now 2014-10-02 reg -p "from june to july" 2014-06-01 Jun 2014 A $10 $10 B $-10 0 end test test --now 2012-02-03 reg -p "from june to july 2014" 2012-06-01 Jun 2012 A $10 $10 B $-10 0 2012-07-01 Jul 2012 A $10 $10 B $-10 0 2012-08-01 Aug 2012 A $10 $10 B $-10 0 2013-06-01 Jun 2013 A $10 $10 B $-10 0 2013-07-01 Jul 2013 A $10 $10 B $-10 0 2013-08-01 Aug 2013 A $10 $10 B $-10 0 2014-06-01 Jun 2014 A $10 $10 B $-10 0 end test test --now 2013-10-02 reg -p "from june to july 2014" 2013-06-01 Jun 2013 A $10 $10 B $-10 0 2013-07-01 Jul 2013 A $10 $10 B $-10 0 2013-08-01 Aug 2013 A $10 $10 B $-10 0 2014-06-01 Jun 2014 A $10 $10 B $-10 0 end test test --now 2014-01-02 reg -p "from june to july 2014" 2014-06-01 Jun 2014 A $10 $10 B $-10 0 end test test --now 2014-10-02 reg -p "from june to july 2014" 2014-06-01 Jun 2014 A $10 $10 B $-10 0 end test test --now 2012-02-03 reg -p "from june 2012 to july 2014" 2012-06-01 Jun 2012 A $10 $10 B $-10 0 2012-07-01 Jul 2012 A $10 $10 B $-10 0 2012-08-01 Aug 2012 A $10 $10 B $-10 0 2013-06-01 Jun 2013 A $10 $10 B $-10 0 2013-07-01 Jul 2013 A $10 $10 B $-10 0 2013-08-01 Aug 2013 A $10 $10 B $-10 0 2014-06-01 Jun 2014 A $10 $10 B $-10 0 end test test --now 2012-02-03 reg -p "from june 2013 to july 2014" 2013-06-01 Jun 2013 A $10 $10 B $-10 0 2013-07-01 Jul 2013 A $10 $10 B $-10 0 2013-08-01 Aug 2013 A $10 $10 B $-10 0 2014-06-01 Jun 2014 A $10 $10 B $-10 0 end test test --now 2015-02-03 reg -p "from june 2013 to july 2014" 2013-06-01 Jun 2013 A $10 $10 B $-10 0 2013-07-01 Jul 2013 A $10 $10 B $-10 0 2013-08-01 Aug 2013 A $10 $10 B $-10 0 2014-06-01 Jun 2014 A $10 $10 B $-10 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/10D19C11.test�������������������������������������������������������������0000664�0000000�0000000�00000001525�14411236400�0017254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: ./ledger -f doc/sample.dat -E bal liab' shows the Assets account = /^Expenses:Books/ (Liabilities:Taxes) -0.10 ~ Monthly Assets:Bank:Checking $500.00 Income:Salary 2004/05/01 * Checking balance Assets:Bank:Checking $1,000.00 Equity:Opening Balances 2004/05/01 * Investment balance Assets:Brokerage 50 AAPL @ $30.00 Equity:Opening Balances 2004/05/14 * Pay day Assets:Bank:Checking $500.00 Income:Salary 2004/05/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard 2004/05/27 (100) Credit card company Liabilities:MasterCard $20.00 Assets:Bank:Checking test -E bal liabilities $-2.00 Liabilities 0 MasterCard $-2.00 Taxes -------------------- $-2.00 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1102.test�����������������������������������������������������������������0000664�0000000�0000000�00000000415�14411236400�0016671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2015/01/15 * Grocery Store Assets:Cash ¤ -5,00 Expenses:Food test -i /dev/null reg 15-Jan-15 Grocery Store Assets:Cash ¤ -5,00 ¤ -5,00 Expenses:Food ¤ 5,00 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1106.test�����������������������������������������������������������������0000664�0000000�0000000�00000000316�14411236400�0016675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2015/01/20 Payee Assets:Cash ¤ 12,34 Expenses:Food test -F "»%(trim(' Trimmed '))«\n" reg expenses »Trimmed« end test test -F "»%(trim('Trimmed'))«\n" reg expenses »Trimmed« end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1127.test�����������������������������������������������������������������0000664�0000000�0000000�00000000603�14411236400�0016677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test that automated transactions are added to accounts soon enough ; for assertions to work. = expr account =~ /^Assets/ (Foo) 1 2018-06-09 Something Assets $100 Equity 2018-06-09 Assert amount added by automated transaction [Foo] = $100 test bal Foo $100 Foo end test �����������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1132.test�����������������������������������������������������������������0000664�0000000�0000000�00000001040�14411236400�0016667�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������1994/09/02=1994/08/25 * Joe Actif:Courant:BnpCc 126,00 F Actif:Remboursement:Autre -126,00 F = 0,00 F 1994/10/20=1994/08/25 * (3551465) Bill Actif:Remboursement:Autre 126,00 F Actif:Courant:BnpCc test bal -> 1 __ERROR__ While parsing file "$FILE", line 3: While parsing posting: Actif:Remboursement:Autre -126,00 F = 0,00 F ^^^^^^ Error: Balance assertion off by 126,00 F (expected to see -126,00 F) end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1147-a.test���������������������������������������������������������������0000664�0000000�0000000�00000000400�14411236400�0017112�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2000/01/01 Pre-transaction balance A $5.00 = $0.00 B test bal -> 1 __ERROR__ While parsing file "$FILE", line 2: While parsing posting: A $5.00 = $0.00 ^^^^^ Error: Balance assertion off by $-5.00 (expected to see $5.00) end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1147-b.test���������������������������������������������������������������0000664�0000000�0000000�00000000246�14411236400�0017123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2000/01/01 Post-transaction balance A $5.00 = $5.00 B test bal $5.00 A $-5.00 B -------------------- 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1159.test�����������������������������������������������������������������0000664�0000000�0000000�00000001675�14411236400�0016716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2003/09/13 a payee ; sat A $100.00 B 2003/09/14 a payee ; mon A $100.00 B 2004/10/15 a payee ; fri A $50.00 B 2004/10/19 a payee ; tue A $50.00 B ;sun first day of month test reg -p "every 12 months from 2001/04/01" 03-Apr-01 - 04-Mar-31 A $200.00 $200.00 B $-200.00 0 04-Apr-01 - 05-Mar-31 A $100.00 $100.00 B $-100.00 0 end test test reg -p 'every 12 months' 03-Sep-01 - 04-Aug-31 A $200.00 $200.00 B $-200.00 0 04-Sep-01 - 05-Aug-31 A $100.00 $100.00 B $-100.00 0 end test �������������������������������������������������������������������ledger-3.3.2/test/regress/1176.test�����������������������������������������������������������������0000664�0000000�0000000�00000001624�14411236400�0016707�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2016-01-01 Employer Me -5400s Them 5400s 2016-01-01 Employer Me -3600s Them 3600s 2016-01-02 Employer Me -1800s Them 1800s 2016-01-02 Employer Me -30s Them 30s test reg --time-colon 16-Jan-01 Employer Me -1:30h -1:30h Them 1:30h 0 16-Jan-01 Employer Me -1:00h -1:00h Them 1:00h 0 16-Jan-02 Employer Me -30:0m -30:0m Them 30:0m 0 16-Jan-02 Employer Me -30s -30s Them 30s 0 end test ������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1182_1.test���������������������������������������������������������������0000664�0000000�0000000�00000000265�14411236400�0017124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������= 2000/01/01 Test A $1.00 B test bal -> 1 __ERROR__ While parsing file "$FILE", line 1: While parsing automated transaction: > = Error: Expected predicate after '=' end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1182_2.test���������������������������������������������������������������0000664�0000000�0000000�00000000355�14411236400�0017125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2000/01/01 Test A $1.00 B ============ 2000/01/02 Test A $1.00 B test bal -> 1 __ERROR__ While parsing file "$FILE", line 5: While parsing automated transaction: > ============ Error: Expected predicate after '=' end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1187_1.test���������������������������������������������������������������0000664�0000000�0000000�00000001147�14411236400�0017131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2006/08/17 * Au Bon Bec Dépense:Alimentation:Restaurant 100,00 € Passif:Crédit:Banque 2006/08/20 * Retrait Dépense:Liquide 60,00 € Passif:Crédit:Banque -60,00 € Passif:Crédit:Banque 60,00 € = -100,00 € Actif:Courant:Cc -60,00 € test bal -60,00 € Actif:Courant:Cc 160,00 € Dépense 100,00 € Alimentation:Restaurant 60,00 € Liquide -100,00 € Passif:Crédit:Banque -------------------- 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1187_2.test���������������������������������������������������������������0000664�0000000�0000000�00000000720�14411236400�0017126�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2001/01/01 * Balance Actif:Courant:Cc 10000,00 F = 10000,00 F Equity 2002/01/11 * Passage à l'euro Actif:Courant:Cc -10000,00 F = 0,00 F Actif:Courant:Cc 1524,49 € = 1524,49 € Revenu:Devise 10000,00 F Revenu:Devise -1524,49 € test bal 1524,49 € Actif:Courant:Cc -10000,00 F Equity 10000,00 F -1524,49 € Revenu:Devise -------------------- 0 end test ������������������������������������������������ledger-3.3.2/test/regress/1187_3.test���������������������������������������������������������������0000664�0000000�0000000�00000001050�14411236400�0017124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2000/01/01 Multiple posts A $5.00 B $7.20 C $-114.99 D 2000/01/02 Many assertions A $2.00 A $3.00 = $10.00 B $0.80 = $8.00 C $-0.01 = $-115.00 B $1.00 C $1 C $4 = $-110 D 2000/01/03 Incremental A $1.00 = $11.00 A $1.00 = $12.00 A $1.00 = $13.00 D 2000/01/04 Assignment A $2 A = $25 D $-10 D $-2 test bal $25.00 A $9.00 B $-110.00 C $76.00 D -------------------- 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1187_4.test���������������������������������������������������������������0000664�0000000�0000000�00000000756�14411236400�0017141�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2000/01/01 Multiple commodities A $5.00 A 32 F B $-12 C 2000/01/02 Assertions A $1 = $6 A 1 F = 33 F A $1 = $7 A 1 F = 34 F B 3 F = 3 F B $12 = $0 B 1 F = 4 F C 2000/01/03 Assignments A = $0 A -4 F = 30 F B $-7 = $-7 B = 0 F C $14 C 8 F test bal 30 F A $-7.00 B $7.00 -30 F C -------------------- 0 end test ������������������ledger-3.3.2/test/regress/1187_5.test���������������������������������������������������������������0000664�0000000�0000000�00000002440�14411236400�0017132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2013/12/01 * Initial State Crédit:Viseca:MasterCard P1 -618.50 CHF Crédit:Viseca:MasterCard P2 -52.10 CHF Equity:Opening Balances 2013/12/15 * Buy Some Chocolate Dépenses:Nourriture 19.00 EUR ; #1 Crédit:Viseca:MasterCard P1 2013/12/15 * Buy Some Chocolate Crédit:Viseca:MasterCard P1 18.00 EUR ; #2 Recettes:Erreurs 2013/12/23 * Facture Viseca Crédit:Viseca:MasterCard P2 52.10 CHF = 0 ; #3 Crédit:Viseca:MasterCard P1 618.50 CHF = 0 CHF ; #4 Dépenses:Frais:Gestion Comptes 1.50 CHF Crédit:Viseca -672.10 CHF 2014/01/03 * Facture Viseca Crédit:Viseca 672.10 CHF = 0 Actif:Comptes:CP courant test bal -672.10 CHF Actif:Comptes:CP courant -1.00 EUR Crédit:Viseca -1.00 EUR MasterCard P1 1.50 CHF 19.00 EUR Dépenses 1.50 CHF Frais:Gestion Comptes 19.00 EUR Nourriture 670.60 CHF Equity:Opening Balances -18.00 EUR Recettes:Erreurs -------------------- 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1222.test�����������������������������������������������������������������0000664�0000000�0000000�00000001016�14411236400�0016672�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo test reg -> 1 __ERROR__ While parsing file "$FILE", line 1: Error: Illegal option --fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1224.test�����������������������������������������������������������������0000664�0000000�0000000�00000006173�14411236400�0016705�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 test reg -> 1 __ERROR__ While parsing file "$FILE", line 1: While parsing transaction: > 2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Error: Invalid date: 2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1384C1D8.test�������������������������������������������������������������0000664�0000000�0000000�00000000563�14411236400�0017271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@alias OLD1 = NEW1 2012-01-01 Something OLD1 $10.00 Other !alias OLD2 = NEW2 2012-01-01 Something OLD2 $10.00 Other account NEW3 alias OLD3 2012-01-01 Something OLD3 $10.00 Other test bal $10.00 NEW1 $10.00 NEW2 $10.00 NEW3 $-30.00 Other -------------------- 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/13965214.test�������������������������������������������������������������0000664�0000000�0000000�00000001521�14411236400�0017223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Y2008 01/30 A Bank 130 Income 02/01 B Bank 140 Income 02/20 C Bank 150 Income 03/01 D Bank 160 Income test reg 08-Jan-30 A Bank 130 130 Income -130 0 08-Feb-01 B Bank 140 140 Income -140 0 08-Feb-20 C Bank 150 150 Income -150 0 08-Mar-01 D Bank 160 160 Income -160 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/14DB77E7.test�������������������������������������������������������������0000664�0000000�0000000�00000001062�14411236400�0017311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 GBP ;P 2011-01-01 EUR 0.8604 GBP P 2011-02-01 EUR 0.8576 GBP 2011-01-31 * AdSense earnings Assets:Receivable:AdSense 11.00 EUR Income:AdSense 2011-02-28 * AdSense earnings Assets:Receivable:AdSense 10.00 EUR Income:AdSense test reg income:adse -X GBP -H 11-Jan-31 AdSense earnings Income:AdSense -11.00 EUR -11.00 EUR 11-Feb-28 Commodities revalued <Revalued> -9.43 GBP -9.43 GBP 11-Feb-28 AdSense earnings Income:AdSense -8.58 GBP -18.01 GBP end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/15230B79.test�������������������������������������������������������������0000664�0000000�0000000�00000001060�14411236400�0017237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2010-04-02 Opening balance Assets:A 14.75 EUR Assets:B 2.84 GBP Equity:Opening balance test reg 10-Apr-02 Opening balance Assets:A 14.75 EUR 14.75 EUR Assets:B 2.84 GBP 14.75 EUR 2.84 GBP Equity:Opening balance -14.75 EUR 2.84 GBP Equity:Opening balance -2.84 GBP 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/15A80F68.test�������������������������������������������������������������0000664�0000000�0000000�00000000607�14411236400�0017273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: Confusing error message with ledger v3 with invalid input 2008/03/03 A (2 FOO @ 10.00 EUR) = 20.00 EUR B test bal -> 1 __ERROR__ While parsing file "$FILE", line 4: While parsing posting: A (2 FOO @ 10.00 EUR) = 20.00 EUR ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Error: Invalid char '@' end test �������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1626.test�����������������������������������������������������������������0000664�0000000�0000000�00000001100�14411236400�0016674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test period every 1000 years from 1 Sep 2011 to 30 May 2012 --now=2018-06-10 --- Period expression tokens --- TOK_EVERY: every TOK_INT: 1000 TOK_YEARS: years TOK_SINCE: since TOK_INT: 1 TOK_A_MONTH: Sep TOK_INT: 2011 TOK_UNTIL: until TOK_INT: 30 TOK_A_MONTH: May TOK_INT: 2012 END_REACHED: <EOF> --- Before stabilization --- range: from day 1 to day 30 duration: 1000 years --- After stabilization --- range: from day 1 to day 30 start: 18-Jan-01 finish: 18-Jan-30 duration: 1000 years --- Sample dates in range (max. 20) --- 1: 18-Jan-01 -- 18-Jan-29 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1702.test�����������������������������������������������������������������0000664�0000000�0000000�00000000474�14411236400�0016704�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tag Foo assert value =~ /^Bar$/ 2019/01/01 * Payee ;; Foo: Bar Income:Foo $-1 Assets:Cash $1 2019/01/01 * Another Payee Assets:Cash $-1 Expenses:Baz $1 test bal $1 Expenses:Baz $-1 Income:Foo -------------------- 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1703.test�����������������������������������������������������������������0000664�0000000�0000000�00000000365�14411236400�0016704�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ P 2018-10-31 MultifundosPlus R$0 2017-05-03 * Test Assets:A 1 AAA @ R$ 3000 Assets:B test reg assets:a -V --now 2018-12-31 17-May-03 Test Assets:A R$3000 R$3000 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1722.test�����������������������������������������������������������������0000664�0000000�0000000�00000000427�14411236400�0016704�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2003/12/20 Organic Co-op Expenses:Food:Groceries $ 37.50 ; ] [=2004/01/01] Assets:Cash $-37.50 test bal $ -37.50 Assets:Cash $ 37.50 Expenses:Food:Groceries -------------------- 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1723.test�����������������������������������������������������������������0000664�0000000�0000000�00000000105�14411236400�0016676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2017/3/17 deferred posting <deferred posting> test reg end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1753.test�����������������������������������������������������������������0000664�0000000�0000000�00000000346�14411236400�0016710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2018-01-02 * Account name starting with semicolon * ;A 10.00 EUR B test reg -> 1 __ERROR__ While parsing file "$FILE", line 2: While parsing posting: * ;A 10.00 EUR Error: Posting has no account end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1775.test�����������������������������������������������������������������0000664�0000000�0000000�00000001454�14411236400�0016715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2017-02-28 * Test Assets:A 10.00 EUR Assets:B -10.00 EUR 2017-03-30 * Test Assets:A 10.00 EUR Assets:B -10.00 EUR 2018-03-30 * Test Assets:A 10.00 EUR Assets:B -10.00 EUR test reg --input-date-format %F 17-Feb-28 Test Assets:A 10.00 EUR 10.00 EUR Assets:B -10.00 EUR 0 17-Mar-30 Test Assets:A 10.00 EUR 10.00 EUR Assets:B -10.00 EUR 0 18-Mar-30 Test Assets:A 10.00 EUR 10.00 EUR Assets:B -10.00 EUR 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/178501DC.test�������������������������������������������������������������0000664�0000000�0000000�00000001150�14411236400�0017257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: The bal report does not honor -r (ledger bal simon ; would show all accounts, rather than just simon and the related ; account). 2011/10/26 trader joe's simon $-50 alice $-50 expenses:food:groceries test bal -r simon $-50 alice $100 expenses:food:groceries -------------------- $50 end test test reg -r simon 11-Oct-26 trader joe's alice $-50 $-50 expense:food:groceries $100 $50 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1894_1.test���������������������������������������������������������������0000664�0000000�0000000�00000001125�14411236400�0017132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������= /Expenses:Transportation/ and not expr "has_tag(/NOTAX/)" Expenses:Tax 0.2 $account -0.2 2020-05-06 * Test Expenses:Transportation 200.00 EUR Assets:Bank -200.00 EUR 2020-05-06 * Test ; :NOTAX: Expenses:Transportation 300.00 EUR Assets:Bank -300.00 EUR test bal -500.00 EUR Assets:Bank 500.00 EUR Expenses 40.00 EUR Tax 460.00 EUR Transportation -------------------- 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1894_2.test���������������������������������������������������������������0000664�0000000�0000000�00000001172�14411236400�0017135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������= ^Expenses and expr "any(account =~ /^A:Assets/ and R)" [A:Assets:Split] (amount / 2) [B:Assets:Split] (-amount / 2) = ^Expenses and expr "any(account =~ /^B:Assets/ and R)" [B:Assets:Split] (amount / 2) [A:Assets:Split] (-amount / 2) 2020-05-08 * Test Expenses:Transportation 200.00 EUR A:Assets:Checking test bal -100.00 EUR A:Assets -200.00 EUR Checking 100.00 EUR Split -100.00 EUR B:Assets:Split 200.00 EUR Expenses:Transportation -------------------- 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1895.test�����������������������������������������������������������������0000664�0000000�0000000�00000001646�14411236400�0016723�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2020-05-18 Test Assets:foo 10.00 EUR Assets:bar -10.00 EUR test bal 0 Assets -10.00 EUR bar 10.00 EUR foo -------------------- 0 end test test bal --invert 0 Assets 10.00 EUR bar -10.00 EUR foo -------------------- 0 end test test reg --format="%(account) %10(amount) %10(amount_expr) %10(total) %10(display_amount) %10(display_total)\n" Assets:foo 10.00 EUR 10.00 EUR 10.00 EUR 10.00 EUR 10.00 EUR Assets:bar -10.00 EUR -10.00 EUR 0 -10.00 EUR 0 end test test reg --format="%(account) %10(amount) %10(amount_expr) %10(total) %10(display_amount) %10(display_total)\n" --invert Assets:foo 10.00 EUR -10.00 EUR -10.00 EUR -10.00 EUR -10.00 EUR Assets:bar -10.00 EUR 10.00 EUR 0 10.00 EUR 0 end test ������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1942_a.test���������������������������������������������������������������0000664�0000000�0000000�00000001735�14411236400�0017213�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2018/01/01 * Opening Balance Assets:Checking $1000.00 Equity:Opening Balances -$1000.00 2018/01/01 * Budget [Assets:Budget:Rent] $100.00 [Assets:Budget:Emergency] $200.00 [Assets:Checking] -$300.00 2018/01/01 * Budget [Assets:Budget:Rent] = $100.00 [Assets:Budget:Emergency] = $400.00 [Assets:Bank] -$500.00 test bal Assets -> 1 __ERROR__ While parsing file "$FILE", line 13: While balancing transaction from "$FILE", lines 10-13: > 2018/01/01 * Budget > [Assets:Budget:Rent] = $100.00 > [Assets:Budget:Emergency] = $400.00 > [Assets:Bank] -$500.00 Unbalanced remainder is: $-300.00 Amount to balance against: $200.00 Error: Transaction does not balance end test �����������������������������������ledger-3.3.2/test/regress/1942_b.test���������������������������������������������������������������0000664�0000000�0000000�00000001735�14411236400�0017214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2018/01/01 * Opening Balance Assets:Checking $1000.00 Equity:Opening Balances -$1000.00 2018/01/01 * Budget [Assets:Budget:Rent] $100.00 [Assets:Budget:Emergency] $200.00 [Assets:Checking] -$300.00 2018/01/01 * Budget [Assets:Budget:Rent] $0 = $100.00 [Assets:Budget:Emergency] = $400.00 [Assets:Bank] -$500.00 test bal Assets -> 1 __ERROR__ While parsing file "$FILE", line 13: While balancing transaction from "$FILE", lines 10-13: > 2018/01/01 * Budget > [Assets:Budget:Rent] $0 = $100.00 > [Assets:Budget:Emergency] = $400.00 > [Assets:Bank] -$500.00 Unbalanced remainder is: $-300.00 Amount to balance against: $200.00 Error: Transaction does not balance end test �����������������������������������ledger-3.3.2/test/regress/1942_c.test���������������������������������������������������������������0000664�0000000�0000000�00000001735�14411236400�0017215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2018/01/01 * Opening Balance Assets:Checking $1000.00 Equity:Opening Balances -$1000.00 2018/01/01 * Budget Assets:Budget:Rent $100.00 Assets:Budget:Emergency $200.00 Assets:Checking -$300.00 2018/01/01 * Budget Assets:Budget:Rent = $100.00 Assets:Budget:Emergency = $400.00 Assets:Bank -$500.00 test bal Assets -> 1 __ERROR__ While parsing file "$FILE", line 13: While balancing transaction from "$FILE", lines 10-13: > 2018/01/01 * Budget > Assets:Budget:Rent = $100.00 > Assets:Budget:Emergency = $400.00 > Assets:Bank -$500.00 Unbalanced remainder is: $-300.00 Amount to balance against: $200.00 Error: Transaction does not balance end test �����������������������������������ledger-3.3.2/test/regress/1942_d.test���������������������������������������������������������������0000664�0000000�0000000�00000001743�14411236400�0017215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2018/01/01 * Opening Balance Assets:Checking $1000.00 Equity:Opening Balances -$1000.00 2018/01/01 * Budget Assets:Budget:Rent $100.00 Assets:Budget:Emergency $200.00 Assets:Checking -$300.00 2018/01/01 * Budget Assets:Budget:Rent $0 Assets:Budget:Emergency = $400.00 Assets:Bank -$500.00 test bal Assets -> 1 __ERROR__ While parsing file "$FILE", line 13: While balancing transaction from "$FILE", lines 10-13: > 2018/01/01 * Budget > Assets:Budget:Rent $0 > Assets:Budget:Emergency = $400.00 > Assets:Bank -$500.00 Unbalanced remainder is: $-300.00 Amount to balance against: $200.00 Error: Transaction does not balance end test �����������������������������ledger-3.3.2/test/regress/1969.test�����������������������������������������������������������������0000664�0000000�0000000�00000000711�14411236400�0016715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2020-10-30 * Set up liability Expenses:Test 310.56 EUR Liabilities:Foo -310.56 EUR 2020-10-30 * Pay liability Assets:Bank -416.30 AUD @ 0.746 EUR Liabilities:Foo 2020-10-30 * Put another commodity into the account so the account is always displayed Expenses:Test 10.00 GBP Liabilities:Foo test bal Liabilities:Foo -10.00 GBP Liabilities:Foo end test �������������������������������������������������������ledger-3.3.2/test/regress/1998.test�����������������������������������������������������������������0000664�0000000�0000000�00000000562�14411236400�0016723�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2021-01-02 Same commodity, different values Assets -1 Stock {100 USD} [2021-01-01] Assets 1 Stock {100 EUR} [2021-01-01] Equity test bal --lots 1 Stock {EUR100} [2021/01/01] -1 Stock {USD100} [2021/01/01] Assets -1 Stock {EUR100} [2021/01/01] 1 Stock {USD100} [2021/01/01] Equity -------------------- 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1A546C4D.test�������������������������������������������������������������0000664�0000000�0000000�00000000507�14411236400�0017303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012/02/22 * Testing invalid amount Assets:Cash $1,00.00 Equity:Opening Balances test bal -> 1 __ERROR__ While parsing file "$FILE", line 2: While parsing posting: Assets:Cash $1,00.00 ^^^^^^^^ Error: Incorrect use of thousand-mark comma end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1CF1EEC2.test�������������������������������������������������������������0000664�0000000�0000000�00000001467�14411236400�0017347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/01/01 sample Assets:Super:ARF $1000.00 Assets:Super:CSS $1000.00 Assets:Super:CSS:Contributions $1000.00 Income:Opening Balances 2009/02/01 sample Assets:Super:ARF $1000.00 Assets:Super:CSS $1000.00 Assets:Super:CSS:Contributions $1000.00 Income:Opening Balances 2009/03/01 sample Assets:Super:ARF $1000.00 Assets:Super:CSS $1000.00 Assets:Super:CSS:Contributions $1000.00 Income:Opening Balances test equity assets 2009/03/01 Opening Balances Assets:Super:ARF $3000.00 Assets:Super:CSS $3000.00 Assets:Super:CSS:Contributions $3000.00 Equity:Opening Balances $-9000.00 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/1D275740.test�������������������������������������������������������������0000664�0000000�0000000�00000012742�14411236400�0017251�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1.200,40 € 1999/11/01 * Achat Actif:SSB 125 STK Actif:SSB -1672,42 $ 1999/11/04 * Vente Actif:SSB -125 STK Dépense:SSB:Commissions 55,07 $ Actif:SSB 1821,54 $ 2001/05/01 * Vente Actif:SSEA -188,7974 STK @ 14,200 $ Dépense:SSEA:Commissions 19,60 $ Actif:SSEA 2661,32 $ 2001/12/21 * Achat Actif:LPG 7,34316 AMD @ 200,340 € Actif:LPG -1471,13 € 2002/12/31 * Réinv. des dividendes Actif:LPG 0,03032 AMD @ 135,060 € Revenu:Dividende:AMD -4,10 € 2003/12/31 * Réinv. des dividendes Actif:LPG 0,02356 AMD @ 147,830 € Revenu:Dividende:AMD -3,48 € 2004/02/17 * Vente Actif:LPG -7,39704 AMD @ 148,860 € Actif:LPG 1101,12 € 2005/12/31 * Réinv. des dividendes Actif:LPG 0,87704 LAPD @ 22,680 € Revenu:Dividende:LAPD -19,89 € 2006/06/30 * Achat Actif:CPE 54,7328 PM @ 33,200 € Actif:CPE -1817,13 € 2006/06/30 * Achat Actif:CPE 13,8913 PM @ 33,200 € Actif:CPE -461,19 € 2007/04/01 Achat Actif:SV 0,2087 CE @ 622,900 € Actif:BC -130,00 € 2007/12/27 Vente Actif:SV -0,2086 EA @ 183,800 € Actif:SV 38,34 € 2008/01/01 Achat Actif:SV 0,1757 CE @ 739,900 € Actif:BC -130,00 € 2008/02/01 Achat Actif:SV 3,1863 EA @ 163,200 € Actif:BC -520,00 € 2008/05/01 Achat Actif:SV 0,2599 CE @ 654,100 € Actif:BC -170,00 € 2008/10/30 Vente Actif:SV -0,0405 CD @ 155,800 € Actif:SV 6,31 € 2008/12/31 Vente Actif:SV -0,0357 MFE @ 259,100 € Actif:SV 9,25 € 2009/06/29 Vente Actif:SV -0,0786 CD @ 155,600 € Actif:SV 12,23 € 2009/07/30 Vente Actif:SV -0,0417 MFE @ 321,100 € Actif:SV 13,39 € 2009/08/01 Achat Actif:SV 1,0204 MFE @ 333,200 € Actif:BC -340,00 € 2009/09/29 Vente Actif:SV -0,0415 MFE @ 358,800 € Actif:SV 14,89 € test print 1999/11/01 * Achat Actif:SSB 125,0000 STK Actif:SSB -1672,42 $ 1999/11/04 * Vente Actif:SSB -125,0000 STK Dépense:SSB:Commissions 55,07 $ Actif:SSB 1821,54 $ 2001/05/01 * Vente Actif:SSEA -188,7974 STK @ 14,20 $ Dépense:SSEA:Commissions 19,60 $ Actif:SSEA 2661,32 $ 2001/12/21 * Achat Actif:LPG 7,34316 AMD @ 200,34 € Actif:LPG -1.471,13 € 2002/12/31 * Réinv. des dividendes Actif:LPG 0,03032 AMD @ 135,06 € Revenu:Dividende:AMD -4,10 € 2003/12/31 * Réinv. des dividendes Actif:LPG 0,02356 AMD @ 147,83 € Revenu:Dividende:AMD -3,48 € 2004/02/17 * Vente Actif:LPG -7,39704 AMD @ 148,86 € Actif:LPG 1.101,12 € 2005/12/31 * Réinv. des dividendes Actif:LPG 0,87704 LAPD @ 22,68 € Revenu:Dividende:LAPD -19,89 € 2006/06/30 * Achat Actif:CPE 54,7328 PM @ 33,20 € Actif:CPE -1.817,13 € 2006/06/30 * Achat Actif:CPE 13,8913 PM @ 33,20 € Actif:CPE -461,19 € 2007/04/01 Achat Actif:SV 0,2087 CE @ 622,90 € Actif:BC -130,00 € 2007/12/27 Vente Actif:SV -0,2086 EA @ 183,80 € Actif:SV 38,34 € 2008/01/01 Achat Actif:SV 0,1757 CE @ 739,90 € Actif:BC -130,00 € 2008/02/01 Achat Actif:SV 3,1863 EA @ 163,20 € Actif:BC -520,00 € 2008/05/01 Achat Actif:SV 0,2599 CE @ 654,10 € Actif:BC -170,00 € 2008/10/30 Vente Actif:SV -0,0405 CD @ 155,80 € Actif:SV 6,31 € 2008/12/31 Vente Actif:SV -0,0357 MFE @ 259,10 € Actif:SV 9,25 € 2009/06/29 Vente Actif:SV -0,0786 CD @ 155,60 € Actif:SV 12,23 € 2009/07/30 Vente Actif:SV -0,0417 MFE @ 321,10 € Actif:SV 13,39 € 2009/08/01 Achat Actif:SV 1,0204 MFE @ 333,20 € Actif:BC -340,00 € 2009/09/29 Vente Actif:SV -0,0415 MFE @ 358,80 € Actif:SV 14,89 € end test ������������������������������ledger-3.3.2/test/regress/1E192DF6.test�������������������������������������������������������������0000664�0000000�0000000�00000005633�14411236400�0017316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; -*- ledger -*- D 1000,00 PLN N $ N h N PLN N zł C 1,00 PLN = 1,00 2010-05-19 * ŁUKASZ STELMACH Assets:Checking:Konto24 GBP 200,00 GBP @ 4,8799 PLN ; fikimiki Assets:Checking:Konto<30 -975,98 PLN 2010-05-19 * ŁUKASZ STELMACH Assets:Checking:Konto24 GBP 200,00 GBP @ 4,8799 PLN ; fikimiki Assets:Checking:Konto<30 -975,98 PLN 2010-05-19 * ŁUKASZ STELMACH Assets:Checking:Konto<30 -975,98 PLN @ 0,204922 GBP Assets:Checking:Konto24 GBP 200,00 GBP ; fikimiki 2010-05-19 * ŁUKASZ STELMACH Assets:Checking:Konto<30 -975,98 PLN @ 0,204922 GBP Assets:Checking:Konto24 GBP 200,00 GBP ; fikimiki 2010-05-19 * ŁUKASZ STELMACH Assets:Checking:Konto24 GBP 200,00 GBP @ 4,8799 PLN ; fikimiki Assets:Checking:Konto<30 -975,98 PLN 2010-05-19 * ŁUKASZ STELMACH Assets:Checking:Konto24 GBP 200,00 GBP @ 4,8799 PLN ; fikimiki Assets:Checking:Konto<30 -975,98 PLN test reg 10-May-19 ŁUKASZ STELMACH As:Checkin:Konto24 GBP 200,00 GBP 200,00 GBP Asse:Checking:Konto<30 -975.98 -975.98 200,00 GBP 10-May-19 ŁUKASZ STELMACH As:Checkin:Konto24 GBP 200,00 GBP -975.98 400,00 GBP Asse:Checking:Konto<30 -975.98 -1951.96 400,00 GBP 10-May-19 ŁUKASZ STELMACH Asse:Checking:Konto<30 -975.98 -2927.94 400,00 GBP As:Checkin:Konto24 GBP 200,00 GBP -2927.94 600,00 GBP 10-May-19 ŁUKASZ STELMACH Asse:Checking:Konto<30 -975.98 -3903.92 600,00 GBP As:Checkin:Konto24 GBP 200,00 GBP -3903.92 800,00 GBP 10-May-19 ŁUKASZ STELMACH As:Checkin:Konto24 GBP 200,00 GBP -3903.92 1000,00 GBP Asse:Checking:Konto<30 -975.98 -4879.9 1000,00 GBP 10-May-19 ŁUKASZ STELMACH As:Checkin:Konto24 GBP 200,00 GBP -4879.9 1200,00 GBP Asse:Checking:Konto<30 -975.98 -5855.88 1200,00 GBP end test �����������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2001.test�����������������������������������������������������������������0000664�0000000�0000000�00000000532�14411236400�0016670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2021/4/1 Was Already Working Expenses:Something (3 - 1) Assets:Cash 2021/4/2 Now Fixed Expenses:Something (3-1) Assets:Cash test reg exp 21-Apr-01 Was Already Working Expenses:Something 2 2 21-Apr-02 Now Fixed Expenses:Something 2 4 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2057.test�����������������������������������������������������������������0000664�0000000�0000000�00000000251�14411236400�0016701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������C 1 "" = 1 $ 2021-01-01 Test A $10 B test bal 10 A -10 B -------------------- 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2058_1.test���������������������������������������������������������������0000664�0000000�0000000�00000000151�14411236400�0017121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2021/1/2 Test A $1.00 B test -p 'last %^@' bal -> 1 __ERROR__ Error: Invalid char '%' end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2058_2.test���������������������������������������������������������������0000664�0000000�0000000�00000000303�14411236400�0017121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2021/1/2 Test A $1.00 B test --limit 'date>=[2020/12/31' register A -> 1 __ERROR__ While parsing value expression: (date>=[2020/12/31)&((account =~ /A/)) Error: Missing ']' end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2069.test�����������������������������������������������������������������0000664�0000000�0000000�00000000764�14411236400�0016715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������P 2021-01-01 EUR 1.15 USD 2021-01-01 Test 1 A 100 USD B 2021-01-01 Test 2 A 100 EUR B test -X USD --sort display_amount reg 21-Jan-01 Test 2 B -115 USD -115 USD 21-Jan-01 Test 1 B -100 USD -215 USD A 100 USD -115 USD 21-Jan-01 Test 2 A 115 USD 0 end test ������������ledger-3.3.2/test/regress/2109.test�����������������������������������������������������������������0000664�0000000�0000000�00000001464�14411236400�0016706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2021/11/22 Entry Assets:Foo 100 FOO (Note) {$43.44} Income:Bar 2021/11/22 Entry Assets:Foo 100 FOO (Misc) {$45.44} = 200 FOO Income:Bar 2021/11/22 Entry Assets:Foo $100 Income:Bar 2021/11/22 Balance Check Assets:Foo 0 FOO = 200 FOO test reg 21-Nov-22 Entry Assets:Foo 100 FOO 100 FOO Income:Bar -100 FOO 0 21-Nov-22 Entry Assets:Foo 100 FOO 100 FOO Income:Bar -100 FOO 0 21-Nov-22 Entry Assets:Foo $100 $100 Income:Bar $-100 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2205_a.test���������������������������������������������������������������0000664�0000000�0000000�00000000427�14411236400�0017201�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ commodity h format 1,000.00 h default 2023-03-03 * Opening balance Assets:Time 100 h Equity:Opening balance test bal 100.00 h Assets:Time -100.00 h Equity:Opening balance -------------------- 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2205_b.test���������������������������������������������������������������0000664�0000000�0000000�00000000475�14411236400�0017205�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ C 1.00000000 BTC = 100000000 sat commodity BTC format 1.00000000 BTC 2023-03-03 * Opening balance Assets:Investments 1.00 BTC Equity:Opening balance test bal 1.00000000 BTC Assets:Investments -1.00000000 BTC Equity:Opening balance -------------------- 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2207.test�����������������������������������������������������������������0000664�0000000�0000000�00000000333�14411236400�0016677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ commodity FOO format 1,000.00 FOO commodity $ format $1,000.00 2023/01/03 * Transaction Assets:Brokerage -0.003 FOO @ $16.79 Assets:Checking test bal $0.05 Assets:Checking end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/25A099C9.dat��������������������������������������������������������������0000664�0000000�0000000�00000062027�14411236400�0017073�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2003-2012, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @defgroup math Mathematical objects */ /** * @file amount.h * @author John Wiegley * * @ingroup math * * @brief Basic type for handling commoditized math: amount_t * * An amount is the most basic numerical type in Ledger, and relies on * commodity.h to represent commoditized amounts, which allows Ledger to * handle mathematical expressions involving disparate commodities. * * Amounts can be of virtually infinite size and precision. When * division or multiplication is performed, the precision is * automatically expanded to include as many extra digits as necessary * to avoid losing information. */ #ifndef _AMOUNT_H #define _AMOUNT_H #include "utils.h" #include "times.h" #include "flags.h" namespace ledger { class commodity_t; struct annotation_t; struct keep_details_t; DECLARE_EXCEPTION(amount_error, std::runtime_error); enum parse_flags_enum_t { PARSE_DEFAULT = 0x00, PARSE_PARTIAL = 0x01, PARSE_SINGLE = 0x02, PARSE_NO_MIGRATE = 0x04, PARSE_NO_REDUCE = 0x08, PARSE_NO_ASSIGN = 0x10, PARSE_NO_ANNOT = 0x20, PARSE_OP_CONTEXT = 0x40, PARSE_SOFT_FAIL = 0x80 }; typedef basic_flags_t<parse_flags_enum_t, uint_least8_t> parse_flags_t; /** * @brief Encapsulate infinite-precision commoditized amounts * * Used to represent commoditized infinite-precision numbers, and * uncommoditized, plain numbers. In the commoditized case, commodities * keep track of how they are used, and are always displayed back to the * user after the same fashion. For uncommoditized numbers, no display * truncation is ever done. In both cases, internal precision is always * kept to an excessive degree. */ class amount_t : public ordered_field_operators<amount_t, ordered_field_operators<amount_t, double, ordered_field_operators<amount_t, unsigned long, ordered_field_operators<amount_t, long> > > > { public: /** Ready the amount subsystem for use. @note Normally called by session_t::initialize(). */ static void initialize(); /** Shutdown the amount subsystem and free all resources. @note Normally called by session_t::shutdown(). */ static void shutdown(); static bool is_initialized; /** The amount's decimal precision. */ typedef uint_least16_t precision_t; /** Number of places of precision by which values are extended to avoid losing precision during division and multiplication. */ static const std::size_t extend_by_digits = 6U; /** If amounts should be streamed using to_fullstring() rather than to_string(), so that complete precision is always displayed no matter what the precision of an individual commodity may be. */ static bool stream_fullstrings; protected: void _copy(const amount_t& amt); void _dup(); void _clear(); void _release(); struct bigint_t; bigint_t * quantity; commodity_t * commodity_; public: /** @name Constructors @{ */ /** Creates a value for which is_null() is true, and which has no value or commodity. If used in a value expression it evaluates to zero, and its commodity equals \c commodity_t::null_commodity. */ amount_t() : quantity(NULL), commodity_(NULL) { TRACE_CTOR(amount_t, ""); } /** Convert a double to an amount. As much precision as possible is decoded from the binary floating point number. */ amount_t(const double val); /** Convert an unsigned long to an amount. It's precision is zero. */ amount_t(const unsigned long val); /** Convert a long to an amount. It's precision is zero, and the sign is preserved. */ amount_t(const long val); /** Parse a string as an (optionally commoditized) amount. If no commodity is present, the resulting commodity is \c commodity_t::null_commodity. The number may be of infinite precision. */ explicit amount_t(const string& val) : quantity(NULL) { parse(val); TRACE_CTOR(amount_t, "const string&"); } /** Parse a pointer to a C string as an (optionally commoditized) amount. If no commodity is present, the resulting commodity is \c commodity_t::null_commodity. The number may be of infinite precision. */ explicit amount_t(const char * val) : quantity(NULL) { assert(val); parse(val); TRACE_CTOR(amount_t, "const char *"); } /*@}*/ /** Create an amount whose display precision is never truncated, even if the amount uses a commodity (which normally causes "round on streaming" to occur). This function is mostly used by debugging code and unit tests. This is the proper way to specify \c $100.005, where display of the extra digit precision is required. If a regular constructor were used, the amount would stream as \c $100.01, even though its internal value equals \c $100.005. */ static amount_t exact(const string& value); /** Release the reference count held for the underlying \c amount_t::bigint_t object. */ ~amount_t() { TRACE_DTOR(amount_t); if (quantity) _release(); } /** @name Assignment and copy @{*/ /** Copy an amount object. Copies are very efficient, using a copy-on-write model. Until the copy is changed, it refers to the same memory used by the original via reference counting. The \c amount_t::bigint_t class in amount.cc maintains the reference. */ amount_t(const amount_t& amt) : quantity(NULL) { if (amt.quantity) _copy(amt); else commodity_ = NULL; TRACE_CTOR(amount_t, "copy"); } /** Copy an amount object, applying the given commodity annotation details afterward. This is equivalent to doing a normal copy (@see amount_t(const amount_t&)) and then calling amount_t::annotate(). */ amount_t(const amount_t& amt, const annotation_t& details) : quantity(NULL) { assert(amt.quantity); _copy(amt); annotate(details); TRACE_CTOR(amount_t, "const amount_t&, const annotation_t&"); } /** Assign an amount object. This is like copying if the amount was null beforehand, otherwise the previous value's reference is must be freed. */ amount_t& operator=(const amount_t& amt); amount_t& operator=(const double val) { return *this = amount_t(val); } amount_t& operator=(const unsigned long val) { return *this = amount_t(val); } amount_t& operator=(const long val) { return *this = amount_t(val); } /* Assign a string to an amount. This causes the contents of the string to be parsed, look for a commoditized or uncommoditized amount specifier. */ amount_t& operator=(const string& str) { return *this = amount_t(str); } amount_t& operator=(const char * str) { assert(str); return *this = amount_t(str); } /*@}*/ /** @name Comparison @{ */ /** Compare two amounts, returning a number less than zero if \p amt is greater, exactly zero if they are equal, and greater than zero if \p amt is less. This method is used to implement all of the other comparison methods.*/ int compare(const amount_t& amt) const; /** Test two amounts for equality. First the commodity pointers are quickly tested, then the multi-precision values themselves must be compared. */ bool operator==(const amount_t& amt) const; template <typename T> bool operator==(const T& val) const { return compare(val) == 0; } template <typename T> bool operator<(const T& amt) const { return compare(amt) < 0; } template <typename T> bool operator>(const T& amt) const { return compare(amt) > 0; } /*@}*/ /** @name Binary arithmetic */ /*@{*/ amount_t& operator+=(const amount_t& amt); amount_t& operator-=(const amount_t& amt); amount_t& operator*=(const amount_t& amt) { return multiply(amt); } amount_t& multiply(const amount_t& amt, bool ignore_commodity = false); /** Divide two amounts while extending the precision to preserve the accuracy of the result. For example, if \c 10 is divided by \c 3, the result ends up having a precision of \link amount_t::extend_by_digits \endlink place to avoid losing internal resolution. */ amount_t& operator/=(const amount_t& amt); /*@}*/ /** @name Unary arithmetic @{ */ /** Return an amount's internal precision. To find the precision it should be displayed at -- assuming it was not created using amount_t::exact() -- use the following expression instead: @code amount.commodity().precision() @endcode */ precision_t precision() const; bool keep_precision() const; void set_keep_precision(const bool keep = true) const; precision_t display_precision() const; /** Returns the negated value of an amount. @see operator-() */ amount_t negated() const { amount_t temp(*this); temp.in_place_negate(); return temp; } void in_place_negate(); amount_t operator-() const { return negated(); } /** Returns the absolute value of an amount. Equivalent to: @code (x < * 0) ? - x : x @endcode */ amount_t abs() const { if (sign() < 0) return negated(); return *this; } amount_t inverted() const { amount_t temp(*this); temp.in_place_invert(); return temp; } void in_place_invert(); /** Yields an amount whose display precision when output is truncated to the display precision of its commodity. This is normally the default state of an amount, but if one has become unrounded, this sets the "keep precision" state back to false. @see set_keep_precision */ amount_t rounded() const { amount_t temp(*this); temp.in_place_round(); return temp; } void in_place_round(); /** Yields an amount which has lost all of its extra precision, beyond what the display precision of the commodity would have printed. */ amount_t truncated() const { amount_t temp(*this); temp.in_place_truncate(); return temp; } void in_place_truncate(); /** Yields an amount which has lost all of its extra precision, beyond what the display precision of the commodity would have printed. */ amount_t floored() const { amount_t temp(*this); temp.in_place_floor(); return temp; } void in_place_floor(); /** Yields an amount which has lost all of its extra precision, beyond what the display precision of the commodity would have printed. */ amount_t ceilinged() const { amount_t temp(*this); temp.in_place_ceiling(); return temp; } void in_place_ceiling(); /** Yields an amount whose display precision is never truncated, even though its commodity normally displays only rounded values. */ amount_t unrounded() const { amount_t temp(*this); temp.in_place_unround(); return temp; } void in_place_unround(); /** reduces a value to its most basic commodity form, for amounts that utilize "scaling commodities". For example, an amount of \c 1h after reduction will be \c 3600s. */ amount_t reduced() const { amount_t temp(*this); temp.in_place_reduce(); return temp; } void in_place_reduce(); /** unreduce(), if used with a "scaling commodity", yields the most compact form greater than one. That is, \c 3599s will unreduce to \c 59.98m, while \c 3601 unreduces to \c 1h. */ amount_t unreduced() const { amount_t temp(*this); temp.in_place_unreduce(); return temp; } void in_place_unreduce(); /** Returns the historical value for an amount -- the default moment returns the most recently known price -- based on the price history for the given commodity (or determined automatically, if none is provided). For example, if the amount were <tt>10 AAPL</tt>, and on Apr 10, 2000 each share of \c AAPL was worth \c $10, then calling value() for that moment in time would yield the amount \c $100.00. */ optional<amount_t> value(const datetime_t& moment = datetime_t(), const commodity_t * in_terms_of = NULL) const; optional<amount_t> price() const; /*@}*/ /** @name Truth tests */ /*@{*/ /** Truth tests. An amount may be truth test in several ways: sign() returns an integer less than, greater than, or equal to zero depending on whether the amount is negative, zero, or greater than zero. Note that this function tests the actual value of the amount -- using its internal precision -- and not the display value. To test its display value, use: `round().sign()'. is_nonzero(), or operator bool, returns true if an amount's display value is not zero. is_zero() returns true if an amount's display value is zero. Thus, $0.0001 is considered zero if the current display precision for dollars is two decimal places. is_realzero() returns true if an amount's actual value is zero. Thus, $0.0001 is never considered realzero. is_null() returns true if an amount has no value and no commodity. This only occurs if an uninitialized amount has never been assigned a value. */ int sign() const; operator bool() const { return is_nonzero(); } bool is_nonzero() const { return ! is_zero(); } bool is_zero() const; bool is_realzero() const { return sign() == 0; } bool is_null() const { if (! quantity) { assert(! commodity_); return true; } return false; } /*@}*/ /** @name Conversion */ /*@{*/ /** Conversion methods. An amount may be converted to the same types it can be constructed from -- with the exception of unsigned long. Implicit conversions are not allowed in C++ (though they are in Python), rather the following conversion methods must be called explicitly: to_double([bool]) returns an amount as a double. If the optional boolean argument is true (the default), an exception is thrown if the conversion would lose information. to_long([bool]) returns an amount as a long integer. If the optional boolean argument is true (the default), an exception is thrown if the conversion would lose information. fits_in_long() returns true if to_long() would not lose precision. to_string() returns an amount's "display value" as a string -- after rounding the value according to the commodity's default precision. It is equivalent to: `round().to_fullstring()'. to_fullstring() returns an amount's "internal value" as a string, without any rounding. quantity_string() returns an amount's "display value", but without any commodity. Note that this is different from `number().to_string()', because in that case the commodity has been stripped and the full, internal precision of the amount would be displayed. */ double to_double() const; long to_long() const; bool fits_in_long() const; operator string() const { return to_string(); } string to_string() const; string to_fullstring() const; string quantity_string() const; /*@}*/ /** @name Commodity methods */ /*@{*/ /** The following methods relate to an amount's commodity: commodity() returns an amount's commodity. If the amount has no commodity, the value returned is the `null_commodity'. has_commodity() returns true if the amount has a commodity. set_commodity(commodity_t) sets an amount's commodity to the given value. Note that this merely sets the current amount to that commodity, it does not "observe" the amount for possible changes in the maximum display precision of the commodity, the way that `parse' does. clear_commodity() sets an amount's commodity to null, such that has_commodity() afterwards returns false. number() returns a commodity-less version of an amount. This is useful for accessing just the numeric portion of an amount. */ commodity_t * commodity_ptr() const; commodity_t& commodity() const { return *commodity_ptr(); } bool has_commodity() const; void set_commodity(commodity_t& comm) { if (! quantity) *this = 0L; commodity_ = &comm; } amount_t with_commodity(const commodity_t& comm) const { if (commodity_ == &comm) { return *this; } else { amount_t tmp(*this); tmp.set_commodity(const_cast<commodity_t&>(comm)); return tmp; } } void clear_commodity() { commodity_ = NULL; } amount_t number() const { if (! has_commodity()) return *this; amount_t temp(*this); temp.clear_commodity(); return temp; } /*@}*/ /** @name Commodity annotations */ /*@{*/ /** An amount's commodity may be annotated with special details, such as the price it was purchased for, when it was acquired, or an arbitrary note, identifying perhaps the lot number of an item. annotate_commodity(amount_t price, [datetime_t date, string tag]) sets the annotations for the current amount's commodity. Only the price argument is required, although it can be passed as `none' if no price is desired. commodity_annotated() returns true if an amount's commodity has any annotation details associated with it. annotation_details() returns all of the details of an annotated commodity's annotations. The structure returns will evaluate as boolean false if there are no details. strip_annotations() returns an amount whose commodity's annotations have been stripped. */ void annotate(const annotation_t& details); bool has_annotation() const; annotation_t& annotation(); const annotation_t& annotation() const { return const_cast<amount_t&>(*this).annotation(); } /** If the lot price is considered whenever working with commoditized values. Let's say a user adds two values of the following form: @code 10 AAPL + 10 AAPL {$20} @endcode This expression adds ten shares of Apple stock with another ten shares that were purchased for \c $20 a share. If \c keep_price is false, the result of this expression is an amount equal to <tt>20 AAPL</tt>. If \c keep_price is \c true the expression yields an exception for adding amounts with different commodities. In that case, a \link balance_t \endlink object must be used to store the combined sum. */ amount_t strip_annotations(const keep_details_t& what_to_keep) const; /*@}*/ /** @name Parsing */ /*@{*/ /** The `flags' argument of both parsing may be one or more of the following: PARSE_NO_MIGRATE means to not pay attention to the way an amount is used. Ordinarily, if an amount were $100.001, for example, it would cause the default display precision for $ to be "widened" to three decimal places. If PARSE_NO_MIGRATE is used, the commodity's default display precision is not changed. PARSE_NO_REDUCE means not to call in_place_reduce() on the resulting amount after it is parsed. These parsing methods observe the amounts they parse (unless PARSE_NO_MIGRATE is true), and set the display details of the corresponding commodity accordingly. This way, amounts do not require commodities to be pre-defined in any way, but merely displays them back to the user in the same fashion as it saw them used. There is also a static convenience method called `parse_conversion' which can be used to define a relationship between scaling commodity values. For example, Ledger uses it to define the relationships among various time values: @code amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes @endcode The method parse() is used to parse an amount from an input stream or a string. A global operator>>() is also defined which simply calls parse on the input stream. The parse() method has two forms: parse(istream, flags_t) parses an amount from the given input stream. parse(string, flags_t) parses an amount from the given string. parse(string, flags_t) also parses an amount from a string. */ bool parse(std::istream& in, const parse_flags_t& flags = PARSE_DEFAULT); bool parse(const string& str, const parse_flags_t& flags = PARSE_DEFAULT) { std::istringstream stream(str); bool result = parse(stream, flags); return result; } static void parse_conversion(const string& larger_str, const string& smaller_str); /*@}*/ /** @name Printing */ /*@{*/ /** An amount may be output to a stream using the `print' method. There is also a global operator<< defined which simply calls print for an amount on the given stream. There is one form of the print method, which takes one required argument and two arguments with default values: print(ostream, bool omit_commodity = false, bool full_precision = false) prints an amounts to the given output stream, using its commodity's default display characteristics. If `omit_commodity' is true, the commodity will not be displayed, only the amount (although the commodity's display precision is still used). If `full_precision' is true, the full internal precision of the amount is displayed, regardless of its commodity's display precision. */ #define AMOUNT_PRINT_NO_FLAGS 0x00 #define AMOUNT_PRINT_RIGHT_JUSTIFY 0x01 #define AMOUNT_PRINT_COLORIZE 0x02 #define AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS 0x04 #define AMOUNT_PRINT_ELIDE_COMMODITY_QUOTES 0x08 void print(std::ostream& out, const uint_least8_t flags = AMOUNT_PRINT_NO_FLAGS) const; /*@}*/ /** @name Debugging */ /*@{*/ /** There are two methods defined to help with debugging: dump(ostream) dumps an amount to an output stream. There is little different from print(), it simply surrounds the display value with a marker, for example "AMOUNT($1.00)". This code is used by other dumping code elsewhere in Ledger. valid() returns true if an amount is valid. This ensures that if an amount has a commodity, it has a valid value pointer, for example, even if that pointer simply points to a zero value. */ void dump(std::ostream& out) const { out << "AMOUNT("; print(out); out << ")"; } bool valid() const; #if HAVE_BOOST_SERIALIZATION private: /** Serialization. */ friend class boost::serialization::access; template<class Archive> void serialize(Archive& ar, const unsigned int /* version */); #endif // HAVE_BOOST_SERIALIZATION /*@}*/ }; inline amount_t amount_t::exact(const string& value) { amount_t temp; temp.parse(value, PARSE_NO_MIGRATE); return temp; } inline string amount_t::to_string() const { std::ostringstream bufstream; print(bufstream); return bufstream.str(); } inline string amount_t::to_fullstring() const { std::ostringstream bufstream; unrounded().print(bufstream); return bufstream.str(); } inline string amount_t::quantity_string() const { std::ostringstream bufstream; number().print(bufstream); return bufstream.str(); } inline std::ostream& operator<<(std::ostream& out, const amount_t& amt) { if (amount_t::stream_fullstrings) amt.unrounded().print(out); else amt.print(out); return out; } inline std::istream& operator>>(std::istream& in, amount_t& amt) { amt.parse(in); return in; } void put_amount(property_tree::ptree& pt, const amount_t& amt, bool wrap = true, bool commodity_details = false); } // namespace ledger #endif // _AMOUNT_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/25A099C9.test�������������������������������������������������������������0000664�0000000�0000000�00000006507�14411236400�0017303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test -f test/regress/25A099C9.dat reg -> 29 __ERROR__ While parsing file "$sourcepath/test/regress/25A099C9.dat", line 1: Error: Directive '/*' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 32: Error: Directive '/**' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 36: Error: Directive '/**' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 66: Error: No quantity specified for amount While parsing file "$sourcepath/test/regress/25A099C9.dat", line 69: Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/test/regress/25A099C9.dat", line 78: Error: Directive '};' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 82: Error: Directive '/**' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 93: Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/test/regress/25A099C9.dat", line 97: Error: Directive '{' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 98: Error: Directive 'public:' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 120: Error: Directive 'protected:' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 131: Error: Directive 'public:' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 711: Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/test/regress/25A099C9.dat", line 740: Error: Directive 'private:' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 749: Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/test/regress/25A099C9.dat", line 750: Error: Directive '};' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 752: Error: Invalid date/time: line amount_t amoun While parsing file "$sourcepath/test/regress/25A099C9.dat", line 756: Error: Directive '}' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 758: Error: Invalid date/time: line string amount_ While parsing file "$sourcepath/test/regress/25A099C9.dat", line 762: Error: Directive '}' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 764: Error: Invalid date/time: line string amount_ While parsing file "$sourcepath/test/regress/25A099C9.dat", line 768: Error: Directive '}' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 770: Error: Invalid date/time: line string amount_ While parsing file "$sourcepath/test/regress/25A099C9.dat", line 774: Error: Directive '}' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 776: Error: Invalid date/time: line std::ostream& While parsing file "$sourcepath/test/regress/25A099C9.dat", line 782: Error: Directive '}' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 783: Error: Invalid date/time: line std::istream& While parsing file "$sourcepath/test/regress/25A099C9.dat", line 786: Error: Directive '}' requires an argument While parsing file "$sourcepath/test/regress/25A099C9.dat", line 789: Error: Unexpected whitespace at beginning of line end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2CE7DADB.test�������������������������������������������������������������0000664�0000000�0000000�00000000411�14411236400�0017355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Buy AAA A 1 AAA @ 1.00 EUR B -1.00 EUR 2012-02-01 * Buy AAA A 1 AAA @ 2.00 EUR B -2.00 EUR test --anon pricedb --format "%(date) %(amount)\n" 2012/01/01 1.00 A 2012/02/01 2.00 A end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/2E3496BD.test�������������������������������������������������������������0000664�0000000�0000000�00000000763�14411236400�0017316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1,000.00 USD 2007-12-31 * Start of year / Opening balances. Account1 1000 EUR @ 1.6 USD Account2 -1000 EUR @ 1.5 USD test bal -> 1 __ERROR__ While parsing file "$FILE", line 5: While balancing transaction from "$FILE", lines 3-5: > 2007-12-31 * Start of year / Opening balances. > Account1 1000 EUR @ 1.6 USD > Account2 -1000 EUR @ 1.5 USD Unbalanced remainder is: 100.00 USD Amount to balance against: 1,600.00 USD Error: Transaction does not balance end test �������������ledger-3.3.2/test/regress/370-budget_period_days.test�����������������������������������������������0000664�0000000�0000000�00000003003�14411236400�0022445�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;budgets care about start dates ~ every 14 days from 2010/02/23 Expenses:Bills:Housecleaning $85.00 Assets:Bank:Checking ;before Sun aug 01 2010 ;now is Mon jun 21 2010 ;a Tuesday? ; test reg --budget --now=2010/06/13 10-Feb-23 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Feb-23 Budget transaction Assets:Bank:Checking $85.00 0 10-Mar-09 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Mar-09 Budget transaction Assets:Bank:Checking $85.00 0 10-Mar-23 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Mar-23 Budget transaction Assets:Bank:Checking $85.00 0 10-Apr-06 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Apr-06 Budget transaction Assets:Bank:Checking $85.00 0 10-Apr-20 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Apr-20 Budget transaction Assets:Bank:Checking $85.00 0 10-May-04 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-May-04 Budget transaction Assets:Bank:Checking $85.00 0 10-May-18 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-May-18 Budget transaction Assets:Bank:Checking $85.00 0 10-Jun-01 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Jun-01 Budget transaction Assets:Bank:Checking $85.00 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/370-budget_period_weeks.test����������������������������������������������0000664�0000000�0000000�00000003245�14411236400�0022633�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;budgets care about start dates ~ every 2 weeks from 2010/02/23 Expenses:Bills:Housecleaning $85.00 Assets:Bank:Checking ;before Sun aug 01 2010 ;now is Mon jun 21 2010 ;a Tuesday? ; test reg --budget --now=2010/06/13 10-Feb-23 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Feb-23 Budget transaction Assets:Bank:Checking $85.00 0 10-Feb-28 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Feb-28 Budget transaction Assets:Bank:Checking $85.00 0 10-Mar-14 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Mar-14 Budget transaction Assets:Bank:Checking $85.00 0 10-Mar-28 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Mar-28 Budget transaction Assets:Bank:Checking $85.00 0 10-Apr-11 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Apr-11 Budget transaction Assets:Bank:Checking $85.00 0 10-Apr-25 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Apr-25 Budget transaction Assets:Bank:Checking $85.00 0 10-May-09 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-May-09 Budget transaction Assets:Bank:Checking $85.00 0 10-May-23 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-May-23 Budget transaction Assets:Bank:Checking $85.00 0 10-Jun-06 Budget transaction Ex:Bills:Housecleaning $-85.00 $-85.00 10-Jun-06 Budget transaction Assets:Bank:Checking $85.00 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/370-forecast_period_days.test���������������������������������������������0000664�0000000�0000000�00000001476�14411236400�0023015�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;does not care about start date ~ every 14 days from 2010/02/23 Expenses:Bills:Housecleaning $85.00 Assets:Bank:Checking ;before Sun aug 01 2010 ;now is Mon jun 21 2010 ;a Tuesday? ;forecasts period start is now first forecast transaction is now + 1 'tomorrow' test reg --forecast 'date <[2010/08/01]' --now=2010/06/13 10-Jun-27 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jun-27 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Jul-11 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jul-11 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Jul-25 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jul-25 Forecast transaction Assets:Bank:Checking $-85.00 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/370-forecast_period_weeks.test��������������������������������������������0000664�0000000�0000000�00000001325�14411236400�0023164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;Tuesday ~ every 2 weeks from 2010/02/23 Expenses:Bills:Housecleaning $85.00 Assets:Bank:Checking ;before Sun aug 01 2010 ;now is Mon jun 21 2010 ;a Sunday test reg --forecast 'date <[2010/08/01]' --now=2010/06/21 10-Jun-27 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jun-27 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Jul-11 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jul-11 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Jul-25 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jul-25 Forecast transaction Assets:Bank:Checking $-85.00 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/370-period.test�����������������������������������������������������������0000664�0000000�0000000�00000025166�14411236400�0020111�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;period days should not be aligned test period 'every 14 days from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 14 TOK_DAYS: days TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 14 days --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 14 days --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Oct-16 2: 11-Oct-17 -- 11-Oct-30 3: 11-Oct-31 -- 11-Nov-13 4: 11-Nov-14 -- 11-Nov-27 5: 11-Nov-28 -- 11-Dec-11 6: 11-Dec-12 -- 11-Dec-25 7: 11-Dec-26 -- 12-Jan-08 8: 12-Jan-09 -- 12-Jan-22 9: 12-Jan-23 -- 12-Feb-05 10: 12-Feb-06 -- 12-Feb-19 11: 12-Feb-20 -- 12-Mar-04 12: 12-Mar-05 -- 12-Mar-18 13: 12-Mar-19 -- 12-Apr-01 14: 12-Apr-02 -- 12-Apr-15 15: 12-Apr-16 -- 12-Apr-29 16: 12-Apr-30 -- 12-May-13 17: 12-May-14 -- 12-May-27 18: 12-May-28 -- 12-Jun-10 19: 12-Jun-11 -- 12-Jun-24 20: 12-Jun-25 -- 12-Jul-08 end test ;ensure weeks behave as before test period 'every 1 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 1 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 1 week --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 1 week --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Oct-08 2: 11-Oct-09 -- 11-Oct-15 3: 11-Oct-16 -- 11-Oct-22 4: 11-Oct-23 -- 11-Oct-29 5: 11-Oct-30 -- 11-Nov-05 6: 11-Nov-06 -- 11-Nov-12 7: 11-Nov-13 -- 11-Nov-19 8: 11-Nov-20 -- 11-Nov-26 9: 11-Nov-27 -- 11-Dec-03 10: 11-Dec-04 -- 11-Dec-10 11: 11-Dec-11 -- 11-Dec-17 12: 11-Dec-18 -- 11-Dec-24 13: 11-Dec-25 -- 11-Dec-31 14: 12-Jan-01 -- 12-Jan-07 15: 12-Jan-08 -- 12-Jan-14 16: 12-Jan-15 -- 12-Jan-21 17: 12-Jan-22 -- 12-Jan-28 18: 12-Jan-29 -- 12-Feb-04 19: 12-Feb-05 -- 12-Feb-11 20: 12-Feb-12 -- 12-Feb-18 end test test period 'every 2 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 2 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 2 weeks --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 2 weeks --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Oct-08 2: 11-Oct-09 -- 11-Oct-22 3: 11-Oct-23 -- 11-Nov-05 4: 11-Nov-06 -- 11-Nov-19 5: 11-Nov-20 -- 11-Dec-03 6: 11-Dec-04 -- 11-Dec-17 7: 11-Dec-18 -- 11-Dec-31 8: 12-Jan-01 -- 12-Jan-14 9: 12-Jan-15 -- 12-Jan-28 10: 12-Jan-29 -- 12-Feb-11 11: 12-Feb-12 -- 12-Feb-25 12: 12-Feb-26 -- 12-Mar-10 13: 12-Mar-11 -- 12-Mar-24 14: 12-Mar-25 -- 12-Apr-07 15: 12-Apr-08 -- 12-Apr-21 16: 12-Apr-22 -- 12-May-05 17: 12-May-06 -- 12-May-19 18: 12-May-20 -- 12-Jun-02 19: 12-Jun-03 -- 12-Jun-16 20: 12-Jun-17 -- 12-Jun-30 end test test period 'every 3 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 3 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 3 weeks --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 3 weeks --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Oct-22 2: 11-Oct-23 -- 11-Nov-12 3: 11-Nov-13 -- 11-Dec-03 4: 11-Dec-04 -- 11-Dec-24 5: 11-Dec-25 -- 12-Jan-14 6: 12-Jan-15 -- 12-Feb-04 7: 12-Feb-05 -- 12-Feb-25 8: 12-Feb-26 -- 12-Mar-17 9: 12-Mar-18 -- 12-Apr-07 10: 12-Apr-08 -- 12-Apr-28 11: 12-Apr-29 -- 12-May-19 12: 12-May-20 -- 12-Jun-09 13: 12-Jun-10 -- 12-Jun-30 14: 12-Jul-01 -- 12-Jul-21 15: 12-Jul-22 -- 12-Aug-11 16: 12-Aug-12 -- 12-Sep-01 17: 12-Sep-02 -- 12-Sep-22 18: 12-Sep-23 -- 12-Oct-13 19: 12-Oct-14 -- 12-Nov-03 20: 12-Nov-04 -- 12-Nov-24 end test test period 'every 4 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 4 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 4 weeks --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 4 weeks --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Oct-22 2: 11-Oct-23 -- 11-Nov-19 3: 11-Nov-20 -- 11-Dec-17 4: 11-Dec-18 -- 12-Jan-14 5: 12-Jan-15 -- 12-Feb-11 6: 12-Feb-12 -- 12-Mar-10 7: 12-Mar-11 -- 12-Apr-07 8: 12-Apr-08 -- 12-May-05 9: 12-May-06 -- 12-Jun-02 10: 12-Jun-03 -- 12-Jun-30 11: 12-Jul-01 -- 12-Jul-28 12: 12-Jul-29 -- 12-Aug-25 13: 12-Aug-26 -- 12-Sep-22 14: 12-Sep-23 -- 12-Oct-20 15: 12-Oct-21 -- 12-Nov-17 16: 12-Nov-18 -- 12-Dec-15 17: 12-Dec-16 -- 13-Jan-12 18: 13-Jan-13 -- 13-Feb-09 19: 13-Feb-10 -- 13-Mar-09 20: 13-Mar-10 -- 13-Apr-06 end test test period 'every 5 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 5 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 5 weeks --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 5 weeks --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Oct-22 2: 11-Oct-23 -- 11-Nov-26 3: 11-Nov-27 -- 11-Dec-31 4: 12-Jan-01 -- 12-Feb-04 5: 12-Feb-05 -- 12-Mar-10 6: 12-Mar-11 -- 12-Apr-14 7: 12-Apr-15 -- 12-May-19 8: 12-May-20 -- 12-Jun-23 9: 12-Jun-24 -- 12-Jul-28 10: 12-Jul-29 -- 12-Sep-01 11: 12-Sep-02 -- 12-Oct-06 12: 12-Oct-07 -- 12-Nov-10 13: 12-Nov-11 -- 12-Dec-15 14: 12-Dec-16 -- 13-Jan-19 15: 13-Jan-20 -- 13-Feb-23 16: 13-Feb-24 -- 13-Mar-30 17: 13-Mar-31 -- 13-May-04 18: 13-May-05 -- 13-Jun-08 19: 13-Jun-09 -- 13-Jul-13 20: 13-Jul-14 -- 13-Aug-17 end test test period 'every 6 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 6 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 6 weeks --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 6 weeks --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Oct-22 2: 11-Oct-23 -- 11-Dec-03 3: 11-Dec-04 -- 12-Jan-14 4: 12-Jan-15 -- 12-Feb-25 5: 12-Feb-26 -- 12-Apr-07 6: 12-Apr-08 -- 12-May-19 7: 12-May-20 -- 12-Jun-30 8: 12-Jul-01 -- 12-Aug-11 9: 12-Aug-12 -- 12-Sep-22 10: 12-Sep-23 -- 12-Nov-03 11: 12-Nov-04 -- 12-Dec-15 12: 12-Dec-16 -- 13-Jan-26 13: 13-Jan-27 -- 13-Mar-09 14: 13-Mar-10 -- 13-Apr-20 15: 13-Apr-21 -- 13-Jun-01 16: 13-Jun-02 -- 13-Jul-13 17: 13-Jul-14 -- 13-Aug-24 18: 13-Aug-25 -- 13-Oct-05 19: 13-Oct-06 -- 13-Nov-16 20: 13-Nov-17 -- 13-Dec-28 end test test period 'every 7 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 7 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 7 weeks --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 7 weeks --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Nov-12 2: 11-Nov-13 -- 11-Dec-31 3: 12-Jan-01 -- 12-Feb-18 4: 12-Feb-19 -- 12-Apr-07 5: 12-Apr-08 -- 12-May-26 6: 12-May-27 -- 12-Jul-14 7: 12-Jul-15 -- 12-Sep-01 8: 12-Sep-02 -- 12-Oct-20 9: 12-Oct-21 -- 12-Dec-08 10: 12-Dec-09 -- 13-Jan-26 11: 13-Jan-27 -- 13-Mar-16 12: 13-Mar-17 -- 13-May-04 13: 13-May-05 -- 13-Jun-22 14: 13-Jun-23 -- 13-Aug-10 15: 13-Aug-11 -- 13-Sep-28 16: 13-Sep-29 -- 13-Nov-16 17: 13-Nov-17 -- 14-Jan-04 18: 14-Jan-05 -- 14-Feb-22 19: 14-Feb-23 -- 14-Apr-12 20: 14-Apr-13 -- 14-May-31 end test test period 'every 8 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 8 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 8 weeks --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 8 weeks --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Nov-19 2: 11-Nov-20 -- 12-Jan-14 3: 12-Jan-15 -- 12-Mar-10 4: 12-Mar-11 -- 12-May-05 5: 12-May-06 -- 12-Jun-30 6: 12-Jul-01 -- 12-Aug-25 7: 12-Aug-26 -- 12-Oct-20 8: 12-Oct-21 -- 12-Dec-15 9: 12-Dec-16 -- 13-Feb-09 10: 13-Feb-10 -- 13-Apr-06 11: 13-Apr-07 -- 13-Jun-01 12: 13-Jun-02 -- 13-Jul-27 13: 13-Jul-28 -- 13-Sep-21 14: 13-Sep-22 -- 13-Nov-16 15: 13-Nov-17 -- 14-Jan-11 16: 14-Jan-12 -- 14-Mar-08 17: 14-Mar-09 -- 14-May-03 18: 14-May-04 -- 14-Jun-28 19: 14-Jun-29 -- 14-Aug-23 20: 14-Aug-24 -- 14-Oct-18 end test test period 'every 9 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 9 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 9 weeks --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 9 weeks --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Nov-12 2: 11-Nov-13 -- 12-Jan-14 3: 12-Jan-15 -- 12-Mar-17 4: 12-Mar-18 -- 12-May-19 5: 12-May-20 -- 12-Jul-21 6: 12-Jul-22 -- 12-Sep-22 7: 12-Sep-23 -- 12-Nov-24 8: 12-Nov-25 -- 13-Jan-26 9: 13-Jan-27 -- 13-Mar-30 10: 13-Mar-31 -- 13-Jun-01 11: 13-Jun-02 -- 13-Aug-03 12: 13-Aug-04 -- 13-Oct-05 13: 13-Oct-06 -- 13-Dec-07 14: 13-Dec-08 -- 14-Feb-08 15: 14-Feb-09 -- 14-Apr-12 16: 14-Apr-13 -- 14-Jun-14 17: 14-Jun-15 -- 14-Aug-16 18: 14-Aug-17 -- 14-Oct-18 19: 14-Oct-19 -- 14-Dec-20 20: 14-Dec-21 -- 15-Feb-21 end test test period 'every 10 weeks from 2011/10/03' --- Period expression tokens --- TOK_EVERY: every TOK_INT: 10 TOK_WEEKS: weeks TOK_SINCE: since TOK_DATE: year 2011 month Oct day 3 END_REACHED: <EOF> --- Before stabilization --- range: from year 2011 month Oct day 3 duration: 10 weeks --- After stabilization --- range: from year 2011 month Oct day 3 start: 11-Oct-03 duration: 10 weeks --- Sample dates in range (max. 20) --- 1: 11-Oct-03 -- 11-Oct-22 2: 11-Oct-23 -- 11-Dec-31 3: 12-Jan-01 -- 12-Mar-10 4: 12-Mar-11 -- 12-May-19 5: 12-May-20 -- 12-Jul-28 6: 12-Jul-29 -- 12-Oct-06 7: 12-Oct-07 -- 12-Dec-15 8: 12-Dec-16 -- 13-Feb-23 9: 13-Feb-24 -- 13-May-04 10: 13-May-05 -- 13-Jul-13 11: 13-Jul-14 -- 13-Sep-21 12: 13-Sep-22 -- 13-Nov-30 13: 13-Dec-01 -- 14-Feb-08 14: 14-Feb-09 -- 14-Apr-19 15: 14-Apr-20 -- 14-Jun-28 16: 14-Jun-29 -- 14-Sep-06 17: 14-Sep-07 -- 14-Nov-15 18: 14-Nov-16 -- 15-Jan-24 19: 15-Jan-25 -- 15-Apr-04 20: 15-Apr-05 -- 15-Jun-13 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/373540CC.test�������������������������������������������������������������0000664�0000000�0000000�00000000340�14411236400�0017256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2004/05/27 Book Store Expenses:Books 20 BOOK @ $10 Liabilities:MasterCard $-200.00 test bal --sort total --flat -X '$' not '(Income|Liabilities)' $200.00 Expenses:Books end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/375.test������������������������������������������������������������������0000664�0000000�0000000�00000001775�14411236400�0016636�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2009-01-01 * Jan 09 Assets:Current 100.00 EUR Income:Salary 2009-02-01 * Feb 09 Assets:Current 100.00 EUR Income:Salary 2009-03-01 * Mar 09 Assets:Current 100.00 EUR Income:Salary 2010-01-01 * Jan 10 Assets:Current 100.00 EUR Income:Salary 2010-02-01 * Feb 10 Assets:Current 100.00 EUR Income:Salary test reg -p "until Feb 2009" 09-Jan-01 Jan 09 Assets:Current 100.00 EUR 100.00 EUR Income:Salary -100.00 EUR 0 end test test reg -p "until February 2009" 09-Jan-01 Jan 09 Assets:Current 100.00 EUR 100.00 EUR Income:Salary -100.00 EUR 0 end test test reg -p "in Feb 2009" 09-Feb-01 Feb 09 Assets:Current 100.00 EUR 100.00 EUR Income:Salary -100.00 EUR 0 end test ���ledger-3.3.2/test/regress/383.test������������������������������������������������������������������0000664�0000000�0000000�00000001370�14411236400�0016624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ --input-date-format %Y:%m:%d D 1000.00 GBP 2014:05:12 * Test Assets:Investments 100 AA {2.00 GBP} [2014:01:01] @@ 200.00 GBP Equity:Opening balance test bal Assets:Investments --lots --date-format %Y.%m.%d 100 AA {2.00 GBP} [2014.01.01] Assets:Investments end test test bal Assets:Investments --lots --date-format %Y/%m/%d 100 AA {2.00 GBP} [2014/01/01] Assets:Investments end test test bal Assets:Investments --lots --date-format %Y:%m:%d 100 AA {2.00 GBP} [2014:01:01] Assets:Investments end test test bal Assets:Investments --lots --date-format %Y-%m-%d 100 AA {2.00 GBP} [2014-01-01] Assets:Investments end test test bal Assets:Investments --lots --date-format %y-%b-%d 100 AA {2.00 GBP} [14-Jan-01] Assets:Investments end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/3AAB00ED.test�������������������������������������������������������������0000664�0000000�0000000�00000001445�14411236400�0017331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: --sort d not working with -p 2009-01-01 Opening Balances Assets:Checking 100.00 EUR Equity:Opening Balances 2009-03-01 Test Expenses:Phone 10.00 EUR Assets:Checking 2009-02-01 Test Expenses:Phone 10.00 EUR Assets:Checking test --sort d -p "until 2010" reg 09-Jan-01 Opening Balances Assets:Checking 100.00 EUR 100.00 EUR Equit:Opening Balances -100.00 EUR 0 09-Feb-01 Test Expenses:Phone 10.00 EUR 10.00 EUR Assets:Checking -10.00 EUR 0 09-Mar-01 Test Expenses:Phone 10.00 EUR 10.00 EUR Assets:Checking -10.00 EUR 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/3AB70168.test�������������������������������������������������������������0000664�0000000�0000000�00000000311�14411236400�0017254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������i 2007/03/01 23:00:00 A o 2007/03/02 01:00:00 i 2007/03/11 23:00:00 B o 2007/03/12 01:00:00 test bal 2.00h A 2.00h B -------------------- 4.00h end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/3FE26304.test�������������������������������������������������������������0000664�0000000�0000000�00000005046�14411236400�0017267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������N $ P 2010/09/28 20:43:24 E $3.700 P 2010/09/28 20:43:25 A $5.230 P 2010/09/28 20:43:26 D $34.020 P 2010/09/28 20:43:27 C $12.370 P 2010/09/28 20:43:28 F $39.700 P 2010/09/28 20:43:29 B $39.430 P 2010/09/29 13:50:15 E $3.720 P 2010/09/29 13:50:15 A $5.240 P 2010/09/29 13:50:17 D $33.920 P 2010/09/29 13:50:18 C $12.310 P 2010/09/29 13:50:18 F $39.670 P 2010/09/29 13:50:19 B $39.830 2010/04/04 * Opening Balance Assets:Sub1 100 A @ $0.01 Equity:Opening Balances 2010/04/04 * Opening Balance Assets:Sub1 100 B @ $32.27 Equity:Opening Balances 2010/04/04 * Opening Balance Assets:Sub1 100 C @ $11.30 Equity:Opening Balances 2010/04/04 * Opening Balance Assets:Sub1 100 D @ $20.30 Equity:Opening Balances 2010/04/04 * Opening Balance Assets:Sub1:Leftovers $6.79 Equity:Opening Balances 2010/04/04 * Opening Balance Assets:Sub1 11 D Equity:Opening Balances 2010/04/04 * Opening Balance Assets:Sub1 100 E @ $2.97 Equity:Opening Balances 2010/05/18=2010/05/21 * FOO Assets:Sub1 200 F @ $27.190 Expenses:Qux $29.95 Assets:Sub2 2010/07/02 * BAR Income:D -$169.65 Assets:Sub2 $32.50 Assets:Sub1 6 D @ $22.64 Assets:Sub1:Leftovers test bal -X \$ sub1 $18026.74 Assets:Sub1 $8.10 Leftovers -------------------- $18026.74 end test test reg -X \$ sub1 --now=2012/03/14 10-Apr-04 Opening Balance Assets:Sub1 $1.00 $1.00 10-Apr-04 Opening Balance Assets:Sub1 $3227.00 $3228.00 10-Apr-04 Opening Balance Assets:Sub1 $1130.00 $4358.00 10-Apr-04 Opening Balance Assets:Sub1 $2030.00 $6388.00 10-Apr-04 Opening Balance Assets:Sub1:Leftovers $6.79 $6394.79 10-Apr-04 Opening Balance Assets:Sub1 $223.30 $6618.09 10-Apr-04 Opening Balance Assets:Sub1 $297.00 $6915.09 10-May-18 FOO Assets:Sub1 $5438.00 $12353.09 10-Jul-02 Commodities revalued <Revalued> $259.74 $12612.83 10-Jul-02 BAR Assets:Sub1 $135.84 $12748.67 Assets:Sub1:Leftovers $1.31 $12749.98 10-Sep-29 Commodities revalued <Revalued> $5251.46 $18001.44 12-Mar-14 Commodities revalued <Revalued> $25.30 $18026.74 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/4509F714.test�������������������������������������������������������������0000664�0000000�0000000�00000001117�14411236400�0017251�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������P 2008/01/01 $ €1 2008/01/11 LIAT Expenses:Travel:Airfare $40.00 Liabilities:MasterCard 2008/02/05 CTX Expenses:Travel:Auto €240.38 Liabilities:MasterCard test bal --exchange=€ €280.38 Expenses:Travel €40.00 Airfare €240.38 Auto €-280.38 Liabilities:MasterCard -------------------- 0 end test test bal --exchange=€ --percent 100.00% Expenses:Travel 14.27% Airfare 85.73% Auto 100.00% Liabilities:MasterCard end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/461980A1.test�������������������������������������������������������������0000664�0000000�0000000�00000000260�14411236400�0017241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 one test:a 1 test:b test bal 0 test 1 a -1 b -------------------- 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/47C579B8.test�������������������������������������������������������������0000664�0000000�0000000�00000005637�14411236400�0017315�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/11 LIAT Expenses:Travel:Airfare 40,00 € Liabilities:MasterCard 2008/01/14 cheaptickets.com Expenses:Travel:Airfare 182,19 € Liabilities:MasterCard 2008/02/05 CTX Expenses:Travel:Auto 240,38 € Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare 238,80 € Liabilities:MasterCard 2008/02/05 UNITED Expenses:Travel:Airfare 238,80 € Liabilities:MasterCard 2008/02/22 BUDGET RENT-A-CAR Expenses:Travel:Auto 40,59 € Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare 1231,60 € Liabilities:MasterCard 2008/03/16 IBERIA Expenses:Travel:Airfare 1231,60 € Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare 155,86 € Liabilities:MasterCard 2008/04/03 AMERICAN Expenses:Travel:Airfare 155,86 € Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare 437,21 € Liabilities:MasterCard 2008/04/30 UNITED Expenses:Travel:Airfare 437,21 € Liabilities:MasterCard 2008/08/08 BCIS I-131 FILING FEE- Expenses:Travel:Passport 170,00 € Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare 912,60 € Liabilities:MasterCard 2008/09/06 AMERICAN Expenses:Travel:Airfare 912,60 € Liabilities:MasterCard 2008/09/22 AGNT FEE Expenses:Travel:Airfare 70,00 € Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare 806,20 € Liabilities:MasterCard 2008/09/22 DELTA Expenses:Travel:Airfare 806,20 € Liabilities:MasterCard 2008/09/22 LIAT 1974 LIMITED Expenses:Travel:Airfare 418,34 € Liabilities:MasterCard 2008/12/26 U.S. Department of State Expenses:Travel:Passport 127,00 € Assets:Checking 2008/12/26 U.S. Department of State Expenses:Travel:Passport 127,00 € Assets:Checking test --decimal-comma --percent balance 100.00% Assets:Checking 100.00% Expenses:Travel 92.15% Airfare 3.13% Auto 4.72% Passport 100.00% Liabilities:MasterCard end test �������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/494-a.ledger��������������������������������������������������������������0000664�0000000�0000000�00000000477�14411236400�0017337�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 15.03.2006 Exxon Expenses:Auto:Gas 10,00 € Liabilities:MasterCard -10,00 € test --input-date-format '%d.%m.%Y' reg 06-Mar-15 Exxon Expenses:Auto:Gas 10,00 € 10,00 € Liabilities:MasterCard -10,00 € 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/494-b.ledger��������������������������������������������������������������0000664�0000000�0000000�00000000477�14411236400�0017340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ --input-date-format %d.%m Y2010 03.01 * Foo A 10.00 EUR B 05.02 * Bar A 20.00 EUR B test reg A 10-Jan-03 Foo A 10.00 EUR 10.00 EUR 10-Feb-05 Bar A 20.00 EUR 30.00 EUR end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/4D9288AE.dat��������������������������������������������������������������0000664�0000000�0000000�00000000107�14411236400�0017105�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-17 Payee Expenses:Food $20 Assets:Cash ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/4D9288AE.py���������������������������������������������������������������0000664�0000000�0000000�00000000166�14411236400�0016772�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import ledger for post in ledger.read_journal("test/regress/4D9288AE.dat").query("^expenses:"): print(post.cost) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/4D9288AE_py.test����������������������������������������������������������0000664�0000000�0000000�00000000063�14411236400�0020025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test python test/regress/4D9288AE.py None end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/516.test������������������������������������������������������������������0000664�0000000�0000000�00000000415�14411236400�0016621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test cleared --file test/input/parsing.dat --cleared-format "%-30(account) %15(get_at(total_expr, 0)) %15(get_at(total_expr, 1))\n%/" Assets $30 0 Income $-30 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/53BCED29.test�������������������������������������������������������������0000664�0000000�0000000�00000001244�14411236400�0017327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D $1,000.00 ; payroll taxes = /^Payroll/ Liabilities:Taxes:CFICA 0.062 Liabilities:Taxes:CMED 0.0145 $account:EFICA -0.062 $account:EMED -0.0145 ; Hourly rates for each employee, as commodity prices. P 2010/01/01 EONE $15.00 ; Payroll transactions 2010/05/18 Payroll from May 2nd to May 15th for Employee1 Assets:Checking 20 EONE Payroll:Employee1 test bal -V $300.00 Assets:Checking $-22.95 Liabilities:Taxes $-18.60 CFICA $-4.35 CMED $-277.05 Payroll:Employee1 $18.60 EFICA $4.35 EMED -------------------- 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/543_a.test����������������������������������������������������������������0000664�0000000�0000000�00000001305�14411236400�0017120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2018/01/01 * Opening Balance Assets:Checking $100.00 Equity:Opening Balances 2018/01/01 * Budget [Assets:Checking] -$100.00 [Assets:Budget:Food:Groceries] $20.00 [Assets:Budget:Food:Restaurants] $80.00 2018/01/02 * Assertion [Assets:Checking] = $0.00 2018/01/02 * Assertion Assets:Checking = $100.00 test bal Assets $100.00 Assets:Budget:Food $20.00 Groceries $80.00 Restaurants -------------------- $100.00 end test test bal Assets -R $100.00 Assets:Checking end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/543_b.test����������������������������������������������������������������0000664�0000000�0000000�00000001270�14411236400�0017122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2018/01/01 * Opening Balance Assets:Checking $100.00 Equity:Opening Balances 2018/01/01 * Budget [Assets:Checking] -$100.00 [Assets:Budget:Food:Groceries] $20.00 [Assets:Budget:Food:Restaurants] $80.00 2018/01/02 * Buy Groceries [Assets:Budget:Food:Groceries] -$20 = $0 [Assets:Checking] $20 Assets:Checking -$20 = $80 Expenses:Food:Groceries $20 test bal Assets $80.00 Assets:Budget:Food:Restaurants end test test bal Assets -R $80.00 Assets:Checking end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/543_c.test����������������������������������������������������������������0000664�0000000�0000000�00000001327�14411236400�0017126�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2018/01/01 * Opening Balance Assets:Checking $100.00 Equity:Opening Balances 2018/01/01 * Budget [Assets:Checking] -$100.00 [Assets:Budget:Food:Groceries] $20.00 [Assets:Budget:Food:Restaurants] $80.00 2018/01/02 * Budget Groceries [Assets:Budget:Food:Groceries] -$20 = $0 [Assets:Checking] $20 2018/01/02 * Buy Groceries Assets:Checking -$20 = $80 Expenses:Food:Groceries $20 test bal Assets $80.00 Assets:Budget:Food:Restaurants end test test bal Assets -R $80.00 Assets:Checking end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/543_d.test����������������������������������������������������������������0000664�0000000�0000000�00000001615�14411236400�0017127�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2018/01/01 * Opening Balance Assets:Checking $100.00 Equity:Opening Balances 2018/01/01 * Budget [Assets:Checking] = 0 [Assets:Budget:Food:Groceries] $20.00 [Assets:Budget:Food:Restaurants] $80.00 2018/01/02 * Groceries Assets:Checking = $80.00 Expenses:Groceries test bal $80.00 Assets $100.00 Budget:Food $20.00 Groceries $80.00 Restaurants $-20.00 Checking $-100.00 Equity:Opening Balances $20.00 Expenses:Groceries -------------------- 0 end test test bal -R $80.00 Assets:Checking $-100.00 Equity:Opening Balances $20.00 Expenses:Groceries -------------------- 0 end test �������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/550-584.test��������������������������������������������������������������0000664�0000000�0000000�00000003507�14411236400�0017142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; this file contains spaces after the payee and tag values. Ledger ; should ignore the trailing spaces for the purposes of determining ; unique values ; ; bug 584 and 550 reported trailing spaces being considered ; significant in payee and tag values. ; ; running ; ledger -f test/spaces.dat payees ; yielded two distinct payees, because utils.h/next_element() didn't ; handle a single space followed by a null correctly. ; ; running ; ledger -f test/spaces.dat reg --group-by "tag('test')" ; yielded four groups. 2011/11/28 * test ; no space after payee or tag value Expenses:misc $1 ; test: spaces Assets:checking 2011/11/28 * test ; single space after payee and tag value Expenses:misc $2 ; test: spaces Assets:checking 2011/11/28 test ; two spaces after payee and tag value Expenses:misc $4 ; test: spaces Assets:checking 2011/11/28 test ; three spaces after payee and tag value Expenses:misc $8 ; test: spaces Assets:checking 2011/11/28 testcommodity ; COM commodity has no space after Expenses:misc 1 COM Assets:checking 2011/11/28 testcommodity ; COM commodity has one space after Expenses:misc 1 COM Assets:checking 2011/11/28 testcommodity ; COM commodity has two spaces after Expenses:misc 1 COM Assets:checking 2011/11/28 testcommodity ; COM commodity has three spaces after Expenses:misc 1 COM Assets:checking test payees test testcommodity end test test reg --group-by "tag('test')" spaces 11-Nov-28 test Expenses:misc $1 $1 11-Nov-28 test Expenses:misc $2 $3 11-Nov-28 test Expenses:misc $4 $7 11-Nov-28 test Expenses:misc $8 $15 end test test commodities $ COM end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/553.test������������������������������������������������������������������0000664�0000000�0000000�00000001033�14411236400�0016617�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Test for missing arguments apply account apply tag apply fixed apply rate apply year test -f - bal -> 5 __ERROR__ While parsing file "", line 2: Error: Directive 'apply account' requires an argument While parsing file "", line 3: Error: Directive 'apply tag' requires an argument While parsing file "", line 4: Error: Directive 'apply fixed' requires an argument While parsing file "", line 5: Error: Directive 'apply rate' requires an argument While parsing file "", line 6: Error: Directive 'apply year' requires an argument end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/55831A79.test�������������������������������������������������������������0000664�0000000�0000000�00000001402�14411236400�0017251�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * previous balances liabilities:credit cards:discover 4462 $-1094.38 equity:opening balances 2008/01/18 * DISCOVER FEES liabilities:credit cards:discover 4462:interest $-36.59 assets:bank:wells fargo:checking 2008/01/18 * DISCOVER FEES liabilities:credit cards:discover 4462:fee $-39 liabilities:credit cards:discover 4462:interest $-28.17 assets:bank:wells fargo:checking 2008/03/01 * discover card payment liabilities:credit cards:discover 4462 $1198.14 assets:bank:wells fargo:checking test bal discover 0 liabilities:credit cards:discover 4462 $-39.00 fee $-64.76 interest -------------------- 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/56BBE69B.test�������������������������������������������������������������0000664�0000000�0000000�00000000603�14411236400�0017331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 USD 2010-01-07 * Put money in Assets:A -20.00 EUR Equity:Opening balances 2010-01-11 * Purchase Assets:A 20.00 EUR @@ 25.00 USD Expenses:B test bal 20.00 EUR Equity:Opening balances -25.00 USD Expenses:B -------------------- 20.00 EUR -25.00 USD end test �����������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/5A03CFC3.test�������������������������������������������������������������0000664�0000000�0000000�00000003544�14411236400�0017323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������= /^Income/ (Liabilities:Tithe) 0.12 ~ Monthly Assets:Checking $500.00 Income:Salary 2003/12/01 * Checking balance Assets:Checking $1,000.00 Equity:Opening Balances 2003/12/20 Organic Co-op Expenses:Food:Groceries $ 37.50 ; [=2004/01/01] Expenses:Food:Groceries $ 37.50 ; [=2004/02/01] Expenses:Food:Groceries $ 37.50 ; [=2004/03/01] Expenses:Food:Groceries $ 37.50 ; [=2004/04/01] Expenses:Food:Groceries $ 37.50 ; [=2004/05/01] Expenses:Food:Groceries $ 37.50 ; [=2004/06/01] Assets:Checking $ -225.00 2003/12/28=2004/01/01 Acme Mortgage Liabilities:Mortgage:Principal $ 200.00 Expenses:Interest:Mortgage $ 500.00 Expenses:Escrow $ 300.00 Assets:Checking $ -1000.00 2004/01/02 Grocery Store Expenses:Food:Groceries $ 65.00 Assets:Checking 2004/01/05 Employer Assets:Checking $ 2000.00 Income:Salary 2004/01/14 Bank ; Regular monthly savings transfer Assets:Savings $ 300.00 Assets:Checking 2004/01/19 Grocery Store Expenses:Food:Groceries $ 44.00 Assets:Checking 2004/01/25 Bank ; Transfer to cover car purchase Assets:Checking $ 5,500.00 Assets:Savings ; :nobudget: 2004/01/25 Tom's Used Cars Expenses:Auto $ 5,500.00 ; :nobudget: Assets:Checking 2004/01/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard 2004/02/01 Sale Assets:Checking:Business $ 30.00 Income:Sales test bal assets $ -3,804.00 Assets $ 1,396.00 Checking $ 30.00 Business $ -5,200.00 Savings -------------------- $ -3,804.00 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/5D92A5EB.test�������������������������������������������������������������0000664�0000000�0000000�00000002444�14411236400�0017332�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly from 2010/7/1 Expenses:Auto:Gas $100.00 Expenses:Auto:Insurance $100.00 Expenses:Childcare $100.00 Expenses:Entertainment:Blizzard $100.00 Expenses:Entertainment:Netflix $100.00 Expenses:Groceries $100.00 Expenses:Utilities:Electric $100.00 Expenses:Utilities:Water $100.00 Expenses:Utilities:Sewage $100.00 Liabilities:Education:ULL $100.00 Liabilities:Mortgage $100.00 Assets:Bank:Checking test -J reg checking -> 1 __ERROR__ While parsing file "$FILE", line 13: While parsing periodic transaction: > ~ Monthly from 2010/7/1 > Expenses:Auto:Gas $100.00 > Expenses:Auto:Insurance $100.00 > Expenses:Childcare $100.00 > Expenses:Entertainment:Blizzard $100.00 > Expenses:Entertainment:Netflix $100.00 > Expenses:Groceries $100.00 > Expenses:Utilities:Electric $100.00 > Expenses:Utilities:Water $100.00 > Expenses:Utilities:Sewage $100.00 > Liabilities:Education:ULL $100.00 > Liabilities:Mortgage $100.00 > Assets:Bank:Checking Error: Posting with null amount's account may be misspelled: "Expenses:Entertainment:Blizzard $100.00" end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/5F1BAF17.test�������������������������������������������������������������0000664�0000000�0000000�00000006442�14411236400�0017330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2006/02/16 b5f40e96 da39a3ee:8d437dbf:ee2bf9bd:f61e33bf $713.35 Assets 2006/03/27 c7ab1f2d da39a3ee:8d437dbf:ee2bf9bd:9b69a35a $139.62 Assets 2006/03/30 f32ea1dc da39a3ee:8d437dbf:ee2bf9bd:d219c681 $1,600.00 Assets 2006/04/21 d449d51e da39a3ee:8d437dbf:ee2bf9bd:75b04b88 $698.10 Assets 2006/05/02 b02f8323 da39a3ee:8d437dbf:ee2bf9bd $42.22 Assets 2006/05/05 2a6f6850 da39a3ee:8d437dbf:ee2bf9bd:75b04b88:b3850e04 $2,468.00 Assets 2006/05/09 59f92263 da39a3ee:8d437dbf:ee2bf9bd:071c929a $132.82 Assets 2006/05/18 f78a7a51 da39a3ee:8d437dbf:ee2bf9bd:e97de844 $368.05 Assets 2006/05/19 f35c594c da39a3ee:8d437dbf:ee2bf9bd:d4e7d7d3 $386.62 Assets 2006/05/22 0233a991 da39a3ee:8d437dbf:ee2bf9bd:9c6e5a3f $141.98 Assets 2006/05/23 56ccae7f da39a3ee:8d437dbf:ee2bf9bd:d31d367b $81.78 Assets 2006/05/24 d449d51e da39a3ee:8d437dbf:ee2bf9bd:75b04b88 $65.00 Assets 2006/05/25 dc833c91 da39a3ee:8d437dbf:ee2bf9bd:d31d367b $34.37 Assets 2006/05/31 6822b496 da39a3ee:8d437dbf:ee2bf9bd:e97de844 $41.78 Assets 2006/06/02 dff8ccb2 da39a3ee:8d437dbf:ee2bf9bd:14e351e1 $67.36 Assets 2006/06/02 e1eb3f4b da39a3ee:8d437dbf:ee2bf9bd:2c7a078f $14.00 Assets 2006/06/04 feec73ea da39a3ee:8d437dbf:ee2bf9bd:131260cb $144.99 Assets 2006/06/04 e8f65e0a da39a3ee:8d437dbf:ee2bf9bd:d219c681 $75.00 Assets 2006/06/04 3fd43f7b da39a3ee:8d437dbf:ee2bf9bd:9c4ba7d0 $239.59 Assets 2006/06/06 754be754 da39a3ee:8d437dbf:ee2bf9bd:35b25929 $1,160.14 Assets 2006/06/06 00c16d44 da39a3ee:8d437dbf:ee2bf9bd:e97de844 $78.45 Assets 2006/06/08 e7b0b317 da39a3ee:8d437dbf:ee2bf9bd $443.00 Assets 2006/07/17 a30c4c31 da39a3ee:8d437dbf:ee2bf9bd $880.80 Assets 2006/08/08 e31a0cf9 da39a3ee:8d437dbf:ee2bf9bd $104.53 Assets 2006/08/10 f0c0c688 da39a3ee:8d437dbf:ee2bf9bd $196.70 Assets 2006/09/12 1bc59c63 da39a3ee:8d437dbf:ee2bf9bd $217.79 Assets 2007/07/16 557e3a00 da39a3ee:8d437dbf:ee2bf9bd:f61e33bf $117.80 Assets test bal da39 --flat --sort amount --display-total amount $14.00 da39a3ee:8d437dbf:ee2bf9bd:2c7a078f $67.36 da39a3ee:8d437dbf:ee2bf9bd:14e351e1 $116.15 da39a3ee:8d437dbf:ee2bf9bd:d31d367b $132.82 da39a3ee:8d437dbf:ee2bf9bd:071c929a $139.62 da39a3ee:8d437dbf:ee2bf9bd:9b69a35a $141.98 da39a3ee:8d437dbf:ee2bf9bd:9c6e5a3f $144.99 da39a3ee:8d437dbf:ee2bf9bd:131260cb $239.59 da39a3ee:8d437dbf:ee2bf9bd:9c4ba7d0 $386.62 da39a3ee:8d437dbf:ee2bf9bd:d4e7d7d3 $488.28 da39a3ee:8d437dbf:ee2bf9bd:e97de844 $763.10 da39a3ee:8d437dbf:ee2bf9bd:75b04b88 $831.15 da39a3ee:8d437dbf:ee2bf9bd:f61e33bf $1,160.14 da39a3ee:8d437dbf:ee2bf9bd:35b25929 $1,675.00 da39a3ee:8d437dbf:ee2bf9bd:d219c681 $1,885.04 da39a3ee:8d437dbf:ee2bf9bd $2,468.00 da39a3ee:8d437dbf:ee2bf9bd:75b04b88:b3850e04 -------------------- 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/5FBF2ED8.test�������������������������������������������������������������0000664�0000000�0000000�00000001161�14411236400�0017352�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * Checking balance Assets:Bank:Checking £0.00 Equity:Opening Balances 2008/02/02 Salary Income:Employer £-334.00 Assets:Bank:Checking $512.85 @@ £334.00 2008/03/02 Salary Income:Employer £-248.07 Assets:Bank:Checking $404.82 @@ £248.07 test bal -B £582.07 Assets:Bank:Checking £-582.07 Income:Employer -------------------- 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/605A410D.test�������������������������������������������������������������0000664�0000000�0000000�00000002423�14411236400�0017253�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������= expr amount > 500 and account =~ /Employer:One/ (Virtual) 1 2012-01-16 KFC Employer:One $1,000.00 Assets:Cash = expr amount>500 and account =~ /Employer:Two/ (Virtual) 10 2012-02-16 KFC Employer:Two $1,000.00 Assets:Cash = Employer:Three and expr amount>500 (Virtual) 100 2012-03-16 KFC Employer:Three $1,000.00 Assets:Cash test reg 12-Jan-16 KFC Employer:One $1,000.00 $1,000.00 Assets:Cash $-1,000.00 0 (Virtual) $1,000.00 $1,000.00 12-Feb-16 KFC Employer:Two $1,000.00 $2,000.00 Assets:Cash $-1,000.00 $1,000.00 (Virtual) $10,000.00 $11,000.00 12-Mar-16 KFC Employer:Three $1,000.00 $12,000.00 Assets:Cash $-1,000.00 $11,000.00 (Virtual) $100,000.00 $111,000.00 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/6188B0EC.test�������������������������������������������������������������0000664�0000000�0000000�00000000422�14411236400�0017304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: No error message if the parser cannot find an included file !include 6188B0EC-does-not-exist.dat test bal -> 1 __ERROR__ While parsing file "$FILE", line 3: Error: File to include was not found: "$sourcepath/test/regress/6188B0EC-does-not-exist.dat" end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/620F0674.test�������������������������������������������������������������0000664�0000000�0000000�00000003762�14411236400�0017254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly since 2010/01/01 Expenses:Bills:Rent $873.00 Expenses:Household $200.00 Income:Salary -$2491.60 Assets:Bank:Checking ~ biweekly from 2010/02/23 Expenses:Bills:Housecleaning $85.00 Assets:Bank:Checking test reg bank --forecast "d<=[next year]" -d "d>=[this month] & d<=[next year]" --sort d --now=2010/06/20 10-Jul-01 Forecast transaction Assets:Bank:Checking $1418.60 $1418.60 10-Jul-04 Forecast transaction Assets:Bank:Checking $-85.00 $1333.60 10-Jul-18 Forecast transaction Assets:Bank:Checking $-85.00 $1248.60 10-Aug-01 Forecast transaction Assets:Bank:Checking $1418.60 $2667.20 10-Aug-01 Forecast transaction Assets:Bank:Checking $-85.00 $2582.20 10-Aug-15 Forecast transaction Assets:Bank:Checking $-85.00 $2497.20 10-Aug-29 Forecast transaction Assets:Bank:Checking $-85.00 $2412.20 10-Sep-01 Forecast transaction Assets:Bank:Checking $1418.60 $3830.80 10-Sep-12 Forecast transaction Assets:Bank:Checking $-85.00 $3745.80 10-Sep-26 Forecast transaction Assets:Bank:Checking $-85.00 $3660.80 10-Oct-01 Forecast transaction Assets:Bank:Checking $1418.60 $5079.40 10-Oct-10 Forecast transaction Assets:Bank:Checking $-85.00 $4994.40 10-Oct-24 Forecast transaction Assets:Bank:Checking $-85.00 $4909.40 10-Nov-01 Forecast transaction Assets:Bank:Checking $1418.60 $6328.00 10-Nov-07 Forecast transaction Assets:Bank:Checking $-85.00 $6243.00 10-Nov-21 Forecast transaction Assets:Bank:Checking $-85.00 $6158.00 10-Dec-01 Forecast transaction Assets:Bank:Checking $1418.60 $7576.60 10-Dec-05 Forecast transaction Assets:Bank:Checking $-85.00 $7491.60 10-Dec-19 Forecast transaction Assets:Bank:Checking $-85.00 $7406.60 11-Jan-01 Forecast transaction Assets:Bank:Checking $1418.60 $8825.20 end test ��������������ledger-3.3.2/test/regress/640D3205.test�������������������������������������������������������������0000664�0000000�0000000�00000000741�14411236400�0017237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: "print" command filters out the "balance assertions" 2008/12/31 * Interest Assets:Brokerage $800.00 Income:Somewhere 2008/12/31 * Interest Assets:Brokerage $200.00 = $1,000.00 Income:Somewhere test print 2008/12/31 * Interest Assets:Brokerage $800.00 Income:Somewhere 2008/12/31 * Interest Assets:Brokerage $200.00 = $1000.00 Income:Somewhere end test �������������������������������ledger-3.3.2/test/regress/647D5DB9.test�������������������������������������������������������������0000664�0000000�0000000�00000000514�14411236400�0017316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 * Purchase Apple shares Equities 1000 AAPL @ $2 Cash 2008/06/30 * Sell some Apple shares Equities -500 AAPL @ $2.5 Cash P 2008/10/01 02:18:02 AAPL $3 P 2009/01/31 02:18:02 AAPL $4 P 3000/01/01 02:18:02 APPL $100 test bal --end 2008/12/31 -JV Equities 2008-12-31 1500 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/65FECA4D.test�������������������������������������������������������������0000664�0000000�0000000�00000000416�14411236400�0017350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--now=2012-02-28 Y 2012 2/29 E-trade Bank Expenses:Food $20 Assets:Cash test reg 12-Feb-29 E-trade Bank Expenses:Food $20 $20 Assets:Cash $-20 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/686.test������������������������������������������������������������������0000664�0000000�0000000�00000002113�14411236400�0016626�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2005/01/03 Opening Balances Assets:Current:Checking $-1,000.00 Liabilities:CredCard $1,000.00 (Virtualaccount) $1,000.00 2005/01/10 carfund Assets:Savings $1550.00 Assets:Checking 2005/01/03 Joe's Expenses:Dining $46.50 Liabilities:CredCard (Virtualaccount2) $46.50 2005/01/05 Another gift Expenses:Gifts $30.00 Liabilities:CredCard 2006/01/03 Gift shop Expenses:Gifts $46.50 Liabilities:CredCard test equity -e 2006 2005/01/10 Opening Balances Assets:Checking $-1,550.00 Assets:Current:Checking $-1,000.00 Assets:Savings $1,550.00 Expenses:Dining $46.50 Expenses:Gifts $30.00 Liabilities:CredCard $923.50 (Virtualaccount) $1,000.00 (Virtualaccount2) $46.50 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/68917252.dat��������������������������������������������������������������0000664�0000000�0000000�00000000055�14411236400�0017026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,amount, 12/12/2011,10,test,extra,fields �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/68917252.test�������������������������������������������������������������0000664�0000000�0000000�00000000262�14411236400�0017235�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test -f /dev/null --input-date-format "%m/%d/%Y" convert test/regress/68917252.dat 2011/12/12 * Expenses:Unknown 10 Equity:Unknown end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/6D9066DD.test�������������������������������������������������������������0000664�0000000�0000000�00000001164�14411236400�0017316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/09/23 * (EFT) Elec Ext Deposit AMAZON.COM FZXXOLTQ - Retail dis payments.amazon.com Assets:Checking $39.05 Assets:Receivable:Amazon 2007/10/15 * FOO ; :USA: Assets:NRL:Checking $1,726.18 Assets:Receivable:CEG ; [2007/10/05] test print 2009/09/23 * (EFT) Elec Ext Deposit AMAZON.COM FZXXOLTQ - Retail dis payments.amazon.com Assets:Checking $39.05 Assets:Receivable:Amazon 2007/10/15 * FOO ; :USA: Assets:NRL:Checking $1,726.18 Assets:Receivable:CEG ; [2007/10/05] end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/6DAB9FE3.test�������������������������������������������������������������0000664�0000000�0000000�00000000355�14411236400�0017354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/01/01 Sample assets 0 FOO @ $8.88 equity test reg -E 09-Jan-01 Sample assets 0 0 equity 0 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/6E041C52.test�������������������������������������������������������������0000664�0000000�0000000�00000000403�14411236400�0017254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-16 KFC Expenses:E of March: End of April $100.00 Assets:Cash test reg 12-Mar-16 KFC Ex:E of: End of April $100.00 $100.00 Assets:Cash $-100.00 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/6E7C2DF9.test�������������������������������������������������������������0000664�0000000�0000000�00000001342�14411236400�0017337�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Y 2010 10/10 * TwentyTen Account:Ten $ 10.10 Assets:Cash apply year 2011 11/11 * TwentyEleven Account:Eleven $ 11.11 Assets:Cash 2012/12/12 * TwentyTwelve Account:Twelve $ 12.12 Assets:Cash 11/11 * TwentyEleven Again Account:Eleven $ 11.11 Assets:Cash test reg --sort date account 10-Oct-10 TwentyTen Account:Ten $ 10.10 $ 10.10 11-Nov-11 TwentyEleven Account:Eleven $ 11.11 $ 21.21 11-Nov-11 TwentyEleven Again Account:Eleven $ 11.11 $ 32.32 12-Dec-12 TwentyTwelve Account:Twelve $ 12.12 $ 44.44 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/712-a.test����������������������������������������������������������������0000664�0000000�0000000�00000000712�14411236400�0017035�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012/09/27 PLN Payment Accrued 3,547.65 PLN {=$0.315786} Accrued 5,320.06 PLN {=$0.315786} Assets:Checking $-2,800.30 test -X $ -V reg 12-Sep-27 PLN Payment Accrued $1,120.30 $1,120.30 Accrued $1,680.00 $2,800.30 Assets:Checking $-2,800.30 0 end test ������������������������������������������������������ledger-3.3.2/test/regress/712-b.test����������������������������������������������������������������0000664�0000000�0000000�00000001250�14411236400�0017034�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2011-01-01 * Opening balance Assets:Cash 10.00 GBP Equity:Opening balance -10.00 GBP 2011-02-01 * Buy 1 AAA for 10.00 GBP Assets:Investments 1 AAA {10.00 GBP} Assets:Cash -10.00 GBP 2011-12-07 * Sell AAA with a gain Assets:Cash 12.00 GBP Assets:Investments -1 AAA {10.00 GBP} @@ 12.00 GBP Income:Capital gains -2.00 GBP test bal 12.00 GBP Assets:Cash -10.00 GBP Equity:Opening balance -2.00 GBP Income:Capital gains -------------------- 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/713-a.test����������������������������������������������������������������0000664�0000000�0000000�00000002241�14411236400�0017035�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2011-01-01 * Opening balance Assets:Cash 25.00 GBP Equity:Opening balance -25.00 GBP 2011-02-01 * Buy 1 AAA for 10.00 GBP Assets:Investments 1 AAA {10.00 GBP} Assets:Cash -10.00 GBP 2011-03-07 * Sell one AAA with a gain Assets:Cash 12.00 GBP Assets:Investments -1 AAA {10.00 GBP} @@ 12.00 GBP Income:Capital gains -2.00 GBP 2011-04-01 * Buy 1 BBB for 15.00 GBP Assets:Investments 1 BBB {15.00 GBP} Assets:Cash -15.00 GBP test bal -B Assets:Investments -p "until 2011-02-20" 10.00 GBP Assets:Investments end test test bal -B Assets:Investments -p "until 2011-03-20" end test test bal -B Assets:Investments -p "until 2011-04-20" 15.00 GBP Assets:Investments end test test bal Assets:Investments -p "until 2011-02-20" 1 AAA Assets:Investments end test test bal Assets:Investments -p "until 2011-03-20" end test test bal Assets:Investments -p "until 2011-04-20" 1 BBB Assets:Investments end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/713-b.test����������������������������������������������������������������0000664�0000000�0000000�00000003171�14411236400�0017041�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2014-01-01 * Opening balances Assets:Cash 100.00 GBP Equity:Opening balances 2014-02-01 * Buy 1 AAA for 10 GBP Assets:Investments 1 AAA @ 10.00 GBP Assets:Cash -10.00 GBP 2014-03-01 * Buy 1 AAA for 20 GBP Assets:Investments 1 AAA @ 20.00 GBP Assets:Cash -20.00 GBP ; Let's say the second purchase attracts an equalisation of 2.00 GBP. ; This means that the purchase price from now on should be 18.00 ; rather than 20.00 GBP. So we add a new share with that price and ; the original date, and remove the existing share at the old price; the ; difference of 2.00 GBP is the equalisation received, which is paid to ; the account. 2014-04-16 * Dividend (Equalisation) from AAA Assets:Investments 1 AAA {18.00 GBP} [2014-03-01] @@ 18.00 GBP Assets:Investments -1 AAA {20.00 GBP} [2014-03-01] @@ 20.00 GBP Assets:Broker 2.00 GBP test bal -B Assets:Investment -p "until 2014-02-20" 10.00 GBP Assets:Investments end test test bal -B Assets:Investment -p "until 2014-03-20" 30.00 GBP Assets:Investments end test test bal -B Assets:Investment -p "until 2014-04-20" 28.00 GBP Assets:Investments end test test bal Assets:Investment -p "until 2014-02-20" 1 AAA Assets:Investments end test test bal Assets:Investment -p "until 2014-03-20" 2 AAA Assets:Investments end test test bal Assets:Investment -p "until 2014-04-20" 2 AAA Assets:Investments end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/727B2DF8.test�������������������������������������������������������������0000664�0000000�0000000�00000010164�14411236400�0017315�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������N $ = /^Expenses:Books/ (Liabilities:Taxes) -0.10 ~ Monthly Assets:Bank:Checking $500.00 Income:Salary 2004/05/01 * Checking balance Assets:Bank:Checking $1,000.00 Equity:Opening Balances 2004/05/03=2004/05/01 * Investment balance Assets:Brokerage 50 AAPL @ $30.00 Equity:Opening Balances 2004/05/14 * Páy dày Assets:Bank:Checking 500.00€ Income:Salary 2004/05/14 * Another dày in which there is Páying Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00 Income:Salary 2004/05/14 * Another dày in which there is Páying Русский язык:Русский язык:Русский язык:Русский язык $1000.00 Income:Salary 2004/05/27 Book Store Expenses:Books $20.00 Expenses:Cards $40.00 Expenses:Docs $30.00 Liabilities:MasterCard 2004/05/27 (100) Credit card company ; This is an xact note! ; Sample: Value Liabilities:MasterCard $20.00 ; This is a posting note! ; Sample: Another Value ; :MyTag: Assets:Bank:Checking ; :AnotherTag: test reg --force-color 04-May-01 Checking balance Assets:Bank:Checking  $1,000.00 $1,000.00 Equit:Opening Balances $-1,000.00 0 04-May-03 Investment balance Assets:Brokerage  50 AAPL 50 AAPL Equit:Opening Balances $-1,500.00 $-1,500.00 50 AAPL 04-May-14 Páy dày Assets:Bank:Checking  500.00€ $-1,500.00 50 AAPL 500.00€ Income:Salary  -500.00€ $-1,500.00 50 AAPL 04-May-14 Another dày in whic.. ..Bá:Ch:As:Bá:Chécking $500.00 $-1,000.00 50 AAPL Income:Salary  $-500.00 $-1,500.00 50 AAPL 04-May-14 Another dày in whic.. Ру:Ру:Рус:Русский язык $1,000.00 $-500.00 50 AAPL Income:Salary  $-1,000.00 $-1,500.00 50 AAPL 04-May-27 Book Store  Expenses:Books  $20.00 $-1,480.00 50 AAPL Expenses:Cards  $40.00 $-1,440.00 50 AAPL Expenses:Docs  $30.00 $-1,410.00 50 AAPL Liabilities:MasterCard $-90.00 $-1,500.00 50 AAPL (Liabilities:Taxes)  $-2.00 $-1,502.00 50 AAPL 04-May-27 Credit card company  Liabilities:MasterCard $20.00 $-1,482.00 50 AAPL Assets:Bank:Checking  $-20.00 $-1,502.00 50 AAPL end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/730.test������������������������������������������������������������������0000664�0000000�0000000�00000001306�14411236400�0016617�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Using -M in combination with an empty result causes a segmentation fault ; therefore this test case does not have or need any test data test -f /dev/null -M reg end test ; Tests mentioned in #730 test reg -M end test test reg -M .foo end test test reg -M -e 2012/01 end test ; Tests mentioned in #1080 test reg '^Expenses' and expr 'any(account =~ /^Assets:Cash/)' --period 'every week this month' end test test bal '^Expenses' and expr 'any(account =~ /^Assets:Cash/)' --period 'every week this month' end test test bal reg foo and expr 'any(account =~ /bar/)' --period 'every week' end test ; Tests mentioned in #1084 test b abc -M end test test reg foo -M end test test bal foo -M end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/751B2357.test�������������������������������������������������������������0000664�0000000�0000000�00000001017�14411236400�0017244�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������01.10.2011 4b4e2a89 ef9d9585:efa1fb7b:22845e93:0e3763f0 2,00 A 2c166ff7:d34e3aa1:8a5075b3:56f3c726 01.10.2011 15983995 eb78b6c0:a2857de3:d6d8ea07:6688fc4e 2,58 A ba3ffe56:c3ba36a5:aa63399f:e9e1d043 test print --date-format=%d.%m.%Y --input-date-format=%d.%m.%Y 01.10.2011 4b4e2a89 ef9d9585:efa1fb7b:22845e93:0e3763f0 2,00 A 2c166ff7:d34e3aa1:8a5075b3:56f3c726 01.10.2011 15983995 eb78b6c0:a2857de3:d6d8ea07:6688fc4e 2,58 A ba3ffe56:c3ba36a5:aa63399f:e9e1d043 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/755.test������������������������������������������������������������������0000664�0000000�0000000�00000001501�14411236400�0016623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ; Test backwards compatibility with ledger2 --date-format %Y/%m/%d 2009-04-17 * Test 1 A 10.00 EUR B 2009-04-18=2010-04-20 (110) Test 2 * C 20.00 EUR ;foo * B test -F "%a\n" reg A B C B end test test -F "%A\n" reg A B C B end test test -F "%d\n" reg 2009/04/17 2009/04/17 2009/04/18=2010/04/20 2009/04/18=2010/04/20 end test test -F "%D\n" reg 2009/04/17 2009/04/17 2009/04/18 2009/04/18 end test test -F "%S\n" reg $FILE $FILE $FILE $FILE end test test -F "%b\n" reg 7 8 11 12 end test test -F "%B\n" reg 90 126 168 209 end test test -F "%X%P\n" reg * Test 1 * Test 1 * Test 2 * Test 2 end test test -F "%Y%P\n" reg * Test 1 * Test 1 Test 2 Test 2 end test test -F "%C%P\n" reg Test 1 Test 1 (110) Test 2 (110) Test 2 end test test -F "%N\n" reg foo end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/777.test������������������������������������������������������������������0000664�0000000�0000000�00000000200�14411236400�0016622�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2012-07-01 * Test A (1/9 FOO) @ 200.00 EUR B -22.22 EUR test bal -22.22 EUR B end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/785.test������������������������������������������������������������������0000664�0000000�0000000�00000003270�14411236400�0016633�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ account AA alias account BB default account CC note account DD payee account EE value account FF assert account GG check account HH eval account II expr commodity AAA alias commodity BBB default commodity CCC nomarket commodity DDD value commodity EEE format commodity FFF note payee FOO alias uuid fooo payee BAR uuid test source -> 14 __ERROR__ While parsing file "$FILE", line 3: Error: Account directive 'alias' requires an argument While parsing file "$FILE", line 9: Error: Account directive 'note' requires an argument While parsing file "$FILE", line 12: Error: Account directive 'payee' requires an argument While parsing file "$FILE", line 15: Error: Account directive 'value' requires an argument While parsing file "$FILE", line 18: Error: Account directive 'assert' requires an argument While parsing file "$FILE", line 21: Error: Account directive 'check' requires an argument While parsing file "$FILE", line 24: Error: Account directive 'eval' requires an argument While parsing file "$FILE", line 27: Error: Account directive 'expr' requires an argument While parsing file "$FILE", line 30: Error: Commodity directive 'alias' requires an argument While parsing file "$FILE", line 39: Error: Commodity directive 'value' requires an argument While parsing file "$FILE", line 42: Error: Commodity directive 'format' requires an argument While parsing file "$FILE", line 45: Error: Commodity directive 'note' requires an argument While parsing file "$FILE", line 48: Error: Payee directive 'alias' requires an argument While parsing file "$FILE", line 52: Error: Payee directive 'uuid' requires an argument end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/786A3DD0.test�������������������������������������������������������������0000664�0000000�0000000�00000000751�14411236400�0017311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 EUR 2011-02-27 * Australia A -100.00 AUD @ 0.746 EUR B 2012-03-12 * Withdrawal Assets:Cash USD 200.00 Expenses:Banking:Fees USD 2.50 Assets:Chequing CAD -203.42 Epenses:Banking:Fees CAD 2.00 Assets:Chequing CAD -2.00 test pricedb --sort date P 2011/02/27 00:00:00 AUD 0.746 EUR P 2012/03/12 00:00:00 USD CAD 1.00454320987654321 end test �����������������������ledger-3.3.2/test/regress/78AB4B87.dat��������������������������������������������������������������0000664�0000000�0000000�00000000475�14411236400�0017113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 EUR P 2011-01-01 GBP 1.2 EUR 2011-01-01 * Opening balance Assets:Bank 10.00 GBP Equity:Opening balance 2012-01-02 * Test Assets:Bank 5.00 GBP Income:Whatever 2012-01-03 * Test Assets:Bank Income:Whatever -5.00 EUR @ 0.8733 GBP ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/78AB4B87.py���������������������������������������������������������������0000664�0000000�0000000�00000001356�14411236400�0016772�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import ledger eur = ledger.commodities.find_or_create('EUR') total_eur = ledger.Amount("0.00 EUR") total_gbp = ledger.Amount("0.00 GBP") total = ledger.Amount("0.00 EUR") for post in ledger.read_journal("test/regress/78AB4B87.dat").query("^income:"): print(post.amount) print(post.amount.commodity) if post.amount.commodity == "EUR": total_eur += post.amount elif post.amount.commodity == "GBP": total_gbp += post.amount a = post.amount.value(eur) if a: print("Total is presently: (%s)" % total) print("Converted to EUR: (%s)" % a) total += a print("Total is now: (%s)" % total) else: print("Cannot convert '%s'" % post.amount) print() print(total) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/78AB4B87_py.test����������������������������������������������������������0000664�0000000�0000000�00000000461�14411236400�0020025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test python test/regress/78AB4B87.py -5.00 GBP GBP Total is presently: (0.00 EUR) Converted to EUR: (-5.73 EUR) Total is now: (-5.73 EUR) -5.00 EUR {0.8733 GBP} [2012/01/03] EUR Total is presently: (-5.73 EUR) Converted to EUR: (-5.00 EUR) Total is now: (-10.73 EUR) -10.73 EUR end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/793F6BF0.test�������������������������������������������������������������0000664�0000000�0000000�00000002463�14411236400�0017321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������N $ = /^Expenses:Books/ (Liabilities:Taxes) -0.10 ~ Monthly Assets:Bank:Checking $500.00 Income:Salary 2004/05/01 * Checking balance Assets:Bank:Checking $1,000.00 Equity:Opening Balances 2004/05/03=2004/05/01 * Investment balance Assets:Brokerage 50 AAPL @ $30.00 Equity:Opening Balances 2004/05/14 * Páy dày Assets:Bank:Checking 500.00€ Income:Salary 2004/05/14 * Another dày in which there is Páying Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00 Income:Salary 2004/05/14 * Another dày in which there is Páying Русский язык:Русский язык:Русский язык:Русский язык $1000.00 Income:Salary 2004/05/27 Book Store Expenses:Books $20.00 Expenses:Cards $40.00 Expenses:Docs $30.00 Liabilities:MasterCard 2004/05/27 (100) Credit card company ; This is an xact note! ; Sample: Value Liabilities:MasterCard $20.00 ; This is a posting note! ; Sample: Another Value ; :MyTag: Assets:Bank:Checking ; :AnotherTag: test entry 2009/03/15 book 10 2009/03/15 Book Store Expenses:Books $10.00 Liabilities:MasterCard end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/7C44010B.test�������������������������������������������������������������0000664�0000000�0000000�00000000676�14411236400�0017263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000,00 € 1994/1/1 Company Assets:Checking 10000 F Income:Salary 1998/1/1 Transfer Assets:US account 200 $ Assets:Checking -1000 F P 1998/12/31 $ 6 F 1999/1/1 Books Expenses:Books 200 $ Assets:US account P 2002/1/1 € 6,55957 F 2002/1/1 Company Assets:Checking 2000 € Income:Salary test reg -X F -J Assets 1994-01-01 10000 1998-01-01 11000 1998-01-01 10000 1998-12-31 10200 1999-01-01 9000 2002-01-01 22119.14 end test ������������������������������������������������������������������ledger-3.3.2/test/regress/7F3650FD.test�������������������������������������������������������������0000664�0000000�0000000�00000003246�14411236400�0017317�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test period --now=2010/11/01 12/01 --- Period expression tokens --- TOK_DATE: month Dec day 1 END_REACHED: <EOF> --- Before stabilization --- range: in month Dec day 1 --- After stabilization --- range: in month Dec day 1 start: 10-Dec-01 finish: 10-Dec-02 --- Sample dates in range (max. 20) --- 1: 10-Dec-01 end test test period --now=2010/11/01 10/01 --- Period expression tokens --- TOK_DATE: month Oct day 1 END_REACHED: <EOF> --- Before stabilization --- range: in month Oct day 1 --- After stabilization --- range: in month Oct day 1 start: 10-Oct-01 finish: 10-Oct-02 --- Sample dates in range (max. 20) --- 1: 10-Oct-01 end test test period --now=2010/11/01 2009/10 --- Period expression tokens --- TOK_DATE: year 2009 month Oct END_REACHED: <EOF> --- Before stabilization --- range: in year 2009 month Oct --- After stabilization --- range: in year 2009 month Oct start: 09-Oct-01 finish: 09-Nov-01 --- Sample dates in range (max. 20) --- 1: 09-Oct-01 end test test period --now=2010/11/01 2009/10/01 --- Period expression tokens --- TOK_DATE: year 2009 month Oct day 1 END_REACHED: <EOF> --- Before stabilization --- range: in year 2009 month Oct day 1 --- After stabilization --- range: in year 2009 month Oct day 1 start: 09-Oct-01 finish: 09-Oct-02 --- Sample dates in range (max. 20) --- 1: 09-Oct-01 end test test period --now=2010/11/01 2009 --- Period expression tokens --- TOK_INT: 2009 END_REACHED: <EOF> --- Before stabilization --- range: in year 2009 --- After stabilization --- range: in year 2009 start: 09-Jan-01 finish: 10-Jan-01 --- Sample dates in range (max. 20) --- 1: 09-Jan-01 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/8254755E.test�������������������������������������������������������������0000664�0000000�0000000�00000000605�14411236400�0017257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly Expenses:Auto:Fuel $120.00 Expenses:Food:Out $100.00 Expenses:Food:Groceries $350.00 Assets 2009/11/01 Expenses:Food:Out $50.00 Assets test bal --flat food:out --now=2009/12/31 $50.00 Expenses:Food:Out end test test bal --flat --budget food:out --now=2009/12/31 $-150.00 Expenses:Food:Out end test ���������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/82763D86.test�������������������������������������������������������������0000664�0000000�0000000�00000001552�14411236400�0017264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: 'ledger -f doc/sample.dat reg -s -n liab' elides too much ; It collapses the account down to "<Total>", even though there was ; only one account! = /^Expenses:Books/ (Liabilities:Taxes) -0.10 ~ Monthly Assets:Bank:Checking $500.00 Income:Salary 2004/05/01 * Checking balance Assets:Bank:Checking $1,000.00 Equity:Opening Balances 2004/05/01 * Investment balance Assets:Brokerage 50 AAPL @ $30.00 Equity:Opening Balances 2004/05/14 * Pay day Assets:Bank:Checking $500.00 Income:Salary 2004/05/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard 2004/05/27 (100) Credit card company Liabilities:MasterCard $20.00 Assets:Bank:Checking test -s reg liabilities 04-May-27 - 04-May-27 (Liabilities:Taxes) $-2.00 $-2.00 end test ������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/83B4A0E5.test�������������������������������������������������������������0000664�0000000�0000000�00000003063�14411236400�0017303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������P 2012-03-01 EUR $2 P 2012-03-01 GBP $2 2012-03-05 KFC Expenses:Food 10 EUR Assets:Cash 2012-03-10 KFC Expenses:Food 10 GBP Assets:Cash test reg food 12-Mar-05 KFC Expenses:Food 10 EUR 10 EUR 12-Mar-10 KFC Expenses:Food 10 GBP 10 EUR 10 GBP end test test reg food -V 12-Mar-05 KFC Expenses:Food $20 $20 12-Mar-10 KFC Expenses:Food $20 $40 end test test reg food -X '$' 12-Mar-05 KFC Expenses:Food $20 $20 12-Mar-10 KFC Expenses:Food $20 $40 end test test reg food -X '$,GBP' 12-Mar-05 KFC Expenses:Food $20 $20 12-Mar-10 KFC Expenses:Food 10 GBP $20 10 GBP end test test reg food -X '$!,GBP' 12-Mar-05 KFC Expenses:Food $20 $20 12-Mar-10 KFC Expenses:Food $20 $40 end test test reg food -X '$,EUR' 12-Mar-05 KFC Expenses:Food 10 EUR 10 EUR 12-Mar-10 KFC Expenses:Food $20 $20 10 EUR end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/854150DF.test�������������������������������������������������������������0000664�0000000�0000000�00000000744�14411236400�0017273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011-11-10 * test A:B:C 12.50 GBP A:C test bal --flat -d "depth>=2" 12.50 GBP A:B:C -12.50 GBP A:C -------------------- 0 end test test bal --flat -d "depth>1" 12.50 GBP A:B:C -12.50 GBP A:C -------------------- 0 end test test bal --flat -d "depth>2" 12.50 GBP A:B:C end test test bal --flat -d "depth==2" -12.50 GBP A:C end test ����������������������������ledger-3.3.2/test/regress/86D2BDC4.test�������������������������������������������������������������0000664�0000000�0000000�00000000704�14411236400�0017327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009/06/03 Westjet Expenses:Transportation:Air C$429.80 @ 1.572865 Expenses:Bank:Fees 2.73 Liabilities:Mastercard test reg -B 09-Jun-03 Westjet Expe:Transportatio:Air 676.017377 676.017377 Expenses:Bank:Fees 2.73 678.747377 Liabilities:Mastercard -678.747377 0 end test ������������������������������������������������������������ledger-3.3.2/test/regress/889BB167.test�������������������������������������������������������������0000664�0000000�0000000�00000000740�14411236400�0017301�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 GBP P 2011-01-01 EUR 0.8604 GBP P 2011-02-01 EUR 0.8576 GBP 2011-01-31 * AdSense earnings Assets:Receivable:AdSense 11.00 EUR Income:AdSense 2011-02-28 * AdSense earnings Assets:Receivable:AdSense 10.00 EUR Income:AdSense test reg income:adse -X GBP -H 11-Jan-31 AdSense earnings Income:AdSense -9.46 GBP -9.46 GBP 11-Feb-28 AdSense earnings Income:AdSense -8.58 GBP -18.04 GBP end test ��������������������������������ledger-3.3.2/test/regress/89233B6D-a.dat������������������������������������������������������������0000664�0000000�0000000�00000000163�14411236400�0017301�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������1994/01/02 * Salary Assets:Bank:Checking 200.00 Income:Salary -200.00 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/89233B6D-b.dat������������������������������������������������������������0000664�0000000�0000000�00000000161�14411236400�0017300�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������1994/01/02 * Rent Expenses:Rent 100.00 Assets:Bank:Checking -100.00 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/89233B6D.test�������������������������������������������������������������0000664�0000000�0000000�00000000431�14411236400�0017270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������!apply account A !include 89233B6D-a.dat !end !apply account B !include 89233B6D-b.dat !end test reg "^A:" 94-Jan-02 Salary A:Assets:Bank:Checking 200 200 A:Income:Salary -200 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/8CE88DB4.test�������������������������������������������������������������0000664�0000000�0000000�00000000502�14411236400�0017334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2010-01-01 * Test Expenses:Food 100.00 EUR Assets:Cash -100.00 EUR 2011-07-30 * Exchange EUR to BAM Assets:Cash -22.00 EUR Assets:Cash 44.00 BAM test pricedb P 2011/07/30 00:00:00 EUR 2.00 BAM end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/8EAF77C0.test�������������������������������������������������������������0000664�0000000�0000000�00000000635�14411236400�0017336�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011/08/05 Rehab Donation Asset:Bank:Boi:Current:Dk 10 Expenses:Misc:Charity 2011/08/07 Net Salary Asset:Bank:Boi:Savings:Dk -3016.24 Income:NetSalary:Dk 2011/08/30 Net Salary Asset:Bank:Boi:Savings:Dk -3016.24 Income:NetSalary:Dk test reg -> 1 __ERROR__ While parsing file "$FILE", line 5: Error: Unexpected whitespace at beginning of line end test ���������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/9188F587.py���������������������������������������������������������������0000664�0000000�0000000�00000001371�14411236400�0016744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import ledger eur = ledger.commodities.find_or_create('EUR') total_eur = ledger.Amount("0.00 EUR") total_gbp = ledger.Amount("0.00 GBP") total = ledger.Amount("0.00 EUR") for post in ledger.read_journal("test/regress/78AB4B87.dat").query("^income:"): print(post.amount) print(post.amount.commodity) if post.amount.commodity == "EUR": total_eur += post.amount elif post.amount.commodity == "GBP": total_gbp += post.amount a = post.amount.value(eur, post.date) if a: print("Total is presently: (%s)" % total) print("Converted to EUR: (%s)" % a) total += a print("Total is now: (%s)" % total) else: print("Cannot convert '%s'" % post.amount) print() print(total) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/9188F587_py.test����������������������������������������������������������0000664�0000000�0000000�00000000461�14411236400�0020002�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test python test/regress/9188F587.py -5.00 GBP GBP Total is presently: (0.00 EUR) Converted to EUR: (-6.00 EUR) Total is now: (-6.00 EUR) -5.00 EUR {0.8733 GBP} [2012/01/03] EUR Total is presently: (-6.00 EUR) Converted to EUR: (-5.00 EUR) Total is now: (-11.00 EUR) -11.00 EUR end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/95350193.test�������������������������������������������������������������0000664�0000000�0000000�00000000232�14411236400�0017225�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011-11-08 * Test Assets:Voucher:Amazon 137.87 GBP (48H5) Assets:Cash -137.87 GBP test pricedb end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/96A8E4A1.test�������������������������������������������������������������0000664�0000000�0000000�00000000344�14411236400�0017311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011-01-31 * Test Expenses:Travel 1 "Spr MegaBonus" Assets:Voucher test -X EUR -H bal -1 "Spr MegaBonus" Assets:Voucher 1 "Spr MegaBonus" Expenses:Travel -------------------- 0 end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/981.test������������������������������������������������������������������0000664�0000000�0000000�00000003665�14411236400�0016641�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ; Test a regex longer than 255 char tag Project check value =~ /^(0ad|ankur|aptosid|archlinux|chakra|debian|debconf14|debconf15|debconf16|drizzle|ffmpeg|ffmpeg|fluxbox|freedesktop|freedombox|gallery|texmacs|haskell|jenkins|libreoffice|madwifi|mingw|openvas|openwrt|openbioinformatics|openembedded|openvoting|osunix|path64|plan9|postgresql|privoxy|smc|helios|tidesdk|tux4kids|yafaray|spi)$/ 2016-01-01 * Test - correct tag Expenses:Hosting 20.00 USD ; Project: debian Assets:Cash -20.00 USD 2016-02-01 * Test - wrong tag Expenses:Hosting 20.00 USD ; Project: foo Assets:Cash -20.00 USD test bal -40.00 USD Assets:Cash 40.00 USD Expenses:Hosting -------------------- 0 __ERROR__ Warning: "$FILE", line 15: Metadata check failed for (Project: foo): (value =~ /^(0ad|ankur|aptosid|archlinux|chakra|debian|debconf14|debconf15|debconf16|drizzle|ffmpeg|ffmpeg|fluxbox|freedesktop|freedombox|gallery|texmacs|haskell|jenkins|libreoffice|madwifi|mingw|openvas|openwrt|openbioinformatics|openembedded|openvoting|osunix|path64|plan9|postgresql|privoxy|smc|helios|tidesdk|tux4kids|yafaray|spi)$/) end test test reg --limit 'payee =~ /XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/' __ERROR__ Warning: "$FILE", line 15: Metadata check failed for (Project: foo): (value =~ /^(0ad|ankur|aptosid|archlinux|chakra|debian|debconf14|debconf15|debconf16|drizzle|ffmpeg|ffmpeg|fluxbox|freedesktop|freedombox|gallery|texmacs|haskell|jenkins|libreoffice|madwifi|mingw|openvas|openwrt|openbioinformatics|openembedded|openvoting|osunix|path64|plan9|postgresql|privoxy|smc|helios|tidesdk|tux4kids|yafaray|spi)$/) end test ���������������������������������������������������������������������������ledger-3.3.2/test/regress/999-a.test����������������������������������������������������������������0000664�0000000�0000000�00000000201�14411236400�0017047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-10 My Brother Assets:Brokerage 1000 AAPL (@) $1 Income:Gifts Received test prices end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/999-b.test����������������������������������������������������������������0000664�0000000�0000000�00000001143�14411236400�0017056�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2014-01-01 * Test Assets:Cash 10.00 GBP Equity:Opening balance -10.00 GBP 2014-02-01 * Exchange rate 1.10 Assets:Cash -1.00 GBP @ 1.10 EUR Assets:Cash 1.10 EUR 2014-03-01 * Exchange rate 1.20 Assets:Cash -1.00 GBP (@) 1.20 EUR Assets:Cash 1.20 EUR test pricedb P 2014/02/01 00:00:00 GBP 1.10 EUR end test test reg Equity:Opening -X EUR 14-Jan-01 Test Equity:Opening balance -10.00 GBP -10.00 GBP 14-Feb-01 Commodities revalued <Revalued> 0 -11.00 EUR end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/9E0E606D.test�������������������������������������������������������������0000664�0000000�0000000�00000001113�14411236400�0017304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 GBP P 2011-02-01 EUR 0.8576 GBP P 2011-03-01 EUR 0.8612 GBP P 2011-04-01 EUR 0.8510 GBP 2011-01-31 * AdSense earnings Assets:Receivable:AdSense 11.00 EUR Income:AdSense 2011-02-28 * AdSense earnings Assets:Receivable:AdSense 10.00 EUR Income:AdSense test reg income:ad -X GBP -H 11-Jan-31 AdSense earnings Income:AdSense -11.00 EUR -11.00 EUR 11-Feb-28 Commodities revalued <Revalued> -9.43 GBP -9.43 GBP 11-Feb-28 AdSense earnings Income:AdSense -8.58 GBP -18.01 GBP end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/9EB10714.test�������������������������������������������������������������0000664�0000000�0000000�00000001772�14411236400�0017271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������1994/01/01 Achat Rialto Actif:Fixe:Rialto 2 Rialto Actif:BNP -120000 € Actif:BNP 120000 € Revenu:Salaire -120000 € P 1995/01/01 Rialto 70000 € P 1996/01/01 Rialto 90000 € P 1997/01/01 Rialto 90000 € P 1998/01/01 Rialto 105000 € P 1999/01/01 Rialto 110000 € P 2000/01/01 Rialto 120000 € P 2001/01/01 Rialto 130000 € P 2002/01/01 Rialto 140000 € P 2003/01/01 Rialto 150000 € P 2004/01/01 Rialto 160000 € P 2005/01/01 Rialto 170000 € P 2006/01/01 Rialto 180000 € P 2007/01/01 Rialto 190000 € P 2008/01/01 Rialto 200000 € P 2009/01/01 Rialto 210000 € P 2010/01/01 Rialto 211000 € test reg -J -V ^Actif 1994-01-01 120000 1994-01-01 0 1994-01-01 120000 1995-01-01 140000 1996-01-01 180000 1998-01-01 210000 1999-01-01 220000 2000-01-01 240000 2001-01-01 260000 2002-01-01 280000 2003-01-01 300000 2004-01-01 320000 2005-01-01 340000 2006-01-01 360000 2007-01-01 380000 2008-01-01 400000 2009-01-01 420000 2010-01-01 422000 end test ������ledger-3.3.2/test/regress/A013A73B.test�������������������������������������������������������������0000664�0000000�0000000�00000000326�14411236400�0017270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2014-01-01 c ; x a 1 b test reg 14-Jan-01 c a 1 1 b -1 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/A28CF697.test�������������������������������������������������������������0000664�0000000�0000000�00000000343�14411236400�0017317�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2010-02-05 * Flight SN2094 Assets:Rewards:Airmiles 125 "M&M" Income:Rewards test print 2010/02/05 * Flight SN2094 Assets:Rewards:Airmiles 125 "M&M" Income:Rewards end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/A3FA7601.dat��������������������������������������������������������������0000664�0000000�0000000�00000000170�14411236400�0017065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������date,posted,code,payee,amount,total,note, 12/12/2011,12/13/2011,100,Test,$10,$20,test, 12/12/2011,12/12/2011,,,$10,$20, ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/A3FA7601.test�������������������������������������������������������������0000664�0000000�0000000�00000000600�14411236400�0017272�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test -f /dev/null --input-date-format '%m/%d/%Y' convert test/regress/A3FA7601.dat 2011/12/12=2011/12/13 * (100) Test ;test Expenses:Unknown $10 Equity:Unknown $-10 = $20 2011/12/12=2011/12/12 * Expenses:Unknown $10 Equity:Unknown $-10 = $20 end test ��������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/A560FDAD.test�������������������������������������������������������������0000664�0000000�0000000�00000012162�14411236400�0017342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Opening balance Assets:Current 17.43 EUR Assets:Investments 200 "LU02" @ 24.77 EUR Assets:Investments 58 "LU02" @ 24.79900855 EUR Equity:Opening balance 2012-01-01 * Opening balance Assets:Pension 785.44 GBP Assets:Pension 97.0017 "H2" @ 5.342999720204 GBP Assets:Pension 4.3441 "H1" @ 5.289999915108 GBP Equity:Opening balance 2012-01-01 * Opening balance: misc Assets:Piggy bank 3.51 GBP Equity:Opening balance 2012-01-01 * Opening balance Assets:Rewards 9836 AAdvantage Equity:Opening balance 2012-01-03 * Receivable Assets:Current Assets:Receivable -161.06 EUR Assets:Receivable -9.99 GBP @@ 11.65 EUR 2012-01-27 * Test Income:Test -2759.50 GBP Income:Test -110.76 GBP Assets:Foo 345.57 GBP Expenses:Test 16.47 GBP Expenses:Test 6.33 GBP Expenses:Test 261.39 GBP Assets:Current test reg -X EUR -H 12-Jan-01 Opening balance Assets:Current 17.43 EUR 17.43 EUR Assets:Investments 4959.80 EUR 4977.23 EUR Assets:Investments 1438.34 EUR 6415.57 EUR Equity:Opening balance -6409.77 EUR 5.80 EUR 12-Jan-01 Opening balance Assets:Pension 785.44 GBP 5.80 EUR 785.44 GBP Assets:Pension 97.0017 H2 5.80 EUR 785.44 GBP 97.0017 H2 Assets:Pension 4.3441 H1 5.80 EUR 785.44 GBP 4.3441 H1 97.0017 H2 Equity:Opening balance -1326.70 GBP 5.80 EUR -541.26 GBP 4.3441 H1 97.0017 H2 12-Jan-01 Opening balance: misc Assets:Piggy bank 3.51 GBP 5.80 EUR -537.75 GBP 4.3441 H1 97.0017 H2 Equity:Opening balance -3.51 GBP 5.80 EUR -541.26 GBP 4.3441 H1 97.0017 H2 12-Jan-01 Opening balance Assets:Rewards 9836 AAdvantage 9836 AAdvantage 5.80 EUR -541.26 GBP 4.3441 H1 97.0017 H2 Equity:Opening balance -9836 AAdvantage 5.80 EUR -541.26 GBP 4.3441 H1 97.0017 H2 12-Jan-03 Commodities revalued <Revalued> 0 5.80 EUR 12-Jan-03 Receivable Assets:Current 172.71 EUR 178.51 EUR Assets:Receivable -161.06 EUR 17.45 EUR Assets:Receivable -11.65 EUR 5.80 EUR 12-Jan-27 Test <Adjustment> 0.01 EUR 5.81 EUR Income:Test -3218.04 EUR -3212.23 EUR <Adjustment> -0.01 EUR -3212.24 EUR Income:Test -129.16 EUR -3341.40 EUR Assets:Foo 402.99 EUR -2938.41 EUR Expenses:Test 19.21 EUR -2919.20 EUR Expenses:Test 7.38 EUR -2911.82 EUR <Adjustment> 0.01 EUR -2911.81 EUR Expenses:Test 304.82 EUR -2606.99 EUR <Adjustment> -0.01 EUR -2607.00 EUR Assets:Current 2612.80 EUR 5.80 EUR end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/A8FCC765.dat��������������������������������������������������������������0000664�0000000�0000000�00000000100�14411236400�0017114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������P 2012-03-16 06:47:12 CAD $2.50 P 2012-03-17 06:47:12 CAD $3.50 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/A8FCC765.test�������������������������������������������������������������0000664�0000000�0000000�00000000301�14411236400�0017326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-17 KFC Expenses:Food 20 CAD Assets:Cash test pricedb --price-db test/regress/A8FCC765.dat P 2012/03/16 06:47:12 CAD $2.5 P 2012/03/17 06:47:12 CAD $3.5 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/AA2FF2B.test��������������������������������������������������������������0000664�0000000�0000000�00000000457�14411236400�0017257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/20 * La Poste Revenu:Invest:Exonéré Actif:Courant:LaPosteLivretA 25,24 € = 25,24 € test --args-only --decimal-comma bal 25,24 € Actif:Courant:LaPosteLivretA -25,24 € Revenu:Invest:Exonéré -------------------- 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/ACE05ECE.test�������������������������������������������������������������0000664�0000000�0000000�00000000260�14411236400�0017356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������i 2011/07/20 17:00:00 Hello Work project o 2011/07/21 01:00:00 Hello test reg Hello 11-Jul-20 Work project (Hello) 8.00h 8.00h end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/AEDE9734.test�������������������������������������������������������������0000664�0000000�0000000�00000000415�14411236400�0017333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011-02-23 Rocket Fuel Expenses:Travel $100000000.00 ; trip: Moon Asset:NASA 2011-02-23 Liquid Oxygen Expenses:Travel $232233223.00 ; trip: Moon Asset:NASA test bal --group-by "tag('trip')" Moon $332233223.00 Expenses:Travel end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/AFAFB804.test�������������������������������������������������������������0000664�0000000�0000000�00000004437�14411236400�0017351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: ledger should allow sorting by multiple criteria, like: ; -S date,payee 2010-02-09 * Z A $10 B 2010-02-09 * Y B $10 C 2010-02-09 * X C $10 D 2010-02-10 * Z A $15 B 2010-02-10 * Y B $15 C 2010-02-10 * X C $15 D test reg -S date,payee 10-Feb-09 X C $10 $10 D $-10 0 10-Feb-09 Y B $10 $10 C $-10 0 10-Feb-09 Z A $10 $10 B $-10 0 10-Feb-10 X C $15 $15 D $-15 0 10-Feb-10 Y B $15 $15 C $-15 0 10-Feb-10 Z A $15 $15 B $-15 0 end test test reg -S payee,date 10-Feb-09 X C $10 $10 D $-10 0 10-Feb-10 X C $15 $15 D $-15 0 10-Feb-09 Y B $10 $10 C $-10 0 10-Feb-10 Y B $15 $15 C $-15 0 10-Feb-09 Z A $10 $10 B $-10 0 10-Feb-10 Z A $15 $15 B $-15 0 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/B21BF389.py���������������������������������������������������������������0000664�0000000�0000000�00000000435�14411236400�0016761�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import ledger for post in ledger.read_journal(__file__.replace(".py", "_py.test")).query("income"): reference = post.tag("Reference") if sys.version_info.major == 2: reference = unicode(reference) print(reference) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/B21BF389_py.test����������������������������������������������������������0000664�0000000�0000000�00000000265�14411236400�0020021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2016/01/11 * Employer Assets:Checking Income:Salary € 1.500,00 ; Reference: Christmas bonus test python test/regress/B21BF389.py Christmas bonus end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/B68FFB0D.test�������������������������������������������������������������0000664�0000000�0000000�00000000417�14411236400�0017351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D $1,000.00 2009/01/01 Sample assets 134.123 FOO @ $8.88 assets 100 BAR @ $8.88 equity test print 2009/01/01 Sample assets 134.123 FOO @ $8.88 assets 100 BAR @ $8.88 equity end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/BBFA1759.test�������������������������������������������������������������0000664�0000000�0000000�00000000517�14411236400�0017331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ test period june 2008 --- Period expression tokens --- TOK_A_MONTH: Jun TOK_INT: 2008 END_REACHED: <EOF> --- Before stabilization --- range: in year 2008 month Jun --- After stabilization --- range: in year 2008 month Jun start: 08-Jun-01 finish: 08-Jul-01 --- Sample dates in range (max. 20) --- 1: 08-Jun-01 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/BF3C1F82-2.test�����������������������������������������������������������0000664�0000000�0000000�00000000654�14411236400�0017470�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Check that include directives are relative for "-f /dev/stdin" include non-existent-ledger-file-BF3C1F82 test -f - reg -> 1 __ERROR__ While parsing file "", line 2: Error: File to include was not found: "$sourcepath/non-existent-ledger-file-BF3C1F82" end test test -f /dev/stdin reg -> 1 __ERROR__ While parsing file "", line 2: Error: File to include was not found: "$sourcepath/non-existent-ledger-file-BF3C1F82" end test ������������������������������������������������������������������������������������ledger-3.3.2/test/regress/BF3C1F82.test�������������������������������������������������������������0000664�0000000�0000000�00000000623�14411236400�0017325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Check that error reporting works for "-f -" 2012/02/30 * Test a 1 b test -f - reg -> 1 __ERROR__ While parsing file "", line 3: While parsing transaction: <no source context> Error: Day of month is not valid for year end test test -f /dev/stdin reg -> 1 __ERROR__ While parsing file "", line 3: While parsing transaction: <no source context> Error: Day of month is not valid for year end test �������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/BFD3FBE1.test�������������������������������������������������������������0000664�0000000�0000000�00000001457�14411236400�0017371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011-01-01 * Opening balance Assets:Investment 100 "AAA" @ 16.58900489 EUR Assets:Investments 5 "BBB" @ 24.79900855 EUR Equity:Opening balance 2011-02-10 * Reimbursement: Taxi / Subway / Bus / Train Assets:A 1.59 GBP Assets:B -1.80 EUR @ 0.884955752212389381 GBP test reg -X EUR -H 11-Jan-01 Opening balance Assets:Investment 1658.90 EUR 1658.90 EUR Assets:Investments 124.00 EUR 1782.90 EUR Equity:Opening balance -1782.90 EUR 0 11-Feb-10 Reimbursement: Taxi.. Assets:A 1.80 EUR 1.80 EUR Assets:B -1.80 EUR 0 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/C0212EAC.test�������������������������������������������������������������0000664�0000000�0000000�00000002713�14411236400�0017311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007-01-01 Opening balances Assets:Cash 10.00 EUR Equity:Opening balances 2008-01-01 Buy 5.00 GBP Assets:Cash 5.00 GBP @ 1.4 EUR Assets:Checking 2009-01-01 Sell 5.00 GBP for 7.50 EUR that I bought for 7.00 EUR Assets:Cash -5.00 GBP {=1.4 EUR} @ 1.5 EUR Assets:Checking 7.50 EUR Income:Gain P 2009-02-01 00:00:00 GBP 1.5 EUR test reg 07-Jan-01 Opening balances Assets:Cash 10.00 EUR 10.00 EUR Equit:Opening balances -10.00 EUR 0 08-Jan-01 Buy 5.00 GBP Assets:Cash 5.00 GBP 5.00 GBP Assets:Checking -7.00 EUR -7.00 EUR 5.00 GBP 09-Jan-01 Sell 5.00 GBP for 7.. Assets:Cash -5.00 GBP {=1.40 EUR} -7.00 EUR 5.00 GBP -5.00 GBP {=1.40 EUR} Assets:Checking 7.50 EUR 0.50 EUR 5.00 GBP -5.00 GBP {=1.40 EUR} Income:Gain -0.50 EUR 5.00 GBP -5.00 GBP {=1.40 EUR} end test �����������������������������������������������������ledger-3.3.2/test/regress/C19E4E9B.test�������������������������������������������������������������0000664�0000000�0000000�00000000641�14411236400�0017334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01=2012-01-02 * Buy AAA A 1 AAA @ 1.00 EUR B -1.00 EUR 2012-02-01 * Buy AAA A 1 AAA @ 2.00 EUR B -2.00 EUR test reg --format "%S: %D %P %t %T\n" $FILE: 2012/01/01 Buy AAA 1 AAA 1 AAA $FILE: 2012/01/01 Buy AAA -1.00 EUR 1 AAA -1.00 EUR $FILE: 2012/02/01 Buy AAA 1 AAA 2 AAA -1.00 EUR $FILE: 2012/02/01 Buy AAA -2.00 EUR 2 AAA -3.00 EUR end test �����������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/C523E23F.test�������������������������������������������������������������0000664�0000000�0000000�00000000701�14411236400�0017300�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1.000,00€ 2009/03/16 * denn's ; Kauf: Yogi-Tee Aufwand:Einkauf:Lebensmittel 17,94€ Aktiva:Bank:Girokonto 2009/03/24 Ansparen Aktiva:Bank:Sparkonto 800,00€ Aktiva:Bank:Girokonto test bal -17,94€ Aktiva:Bank -817,94€ Girokonto 800,00€ Sparkonto 17,94€ Aufwand:Einkauf:Lebensmittel -------------------- 0 end test ���������������������������������������������������������������ledger-3.3.2/test/regress/C927CFFE.test�������������������������������������������������������������0000664�0000000�0000000�00000002724�14411236400�0017363�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2010/02/09 * Test 1 A $10 B 2010/02/10 * Test 2 B $10 C 2010/02/11 * Test 3 C $10 D test reg test -l "date>=[2010/02/10]" reg 10-Feb-10 Test 2 B $10 $10 C $-10 0 10-Feb-11 Test 3 C $10 $10 D $-10 0 end test test -l "date<=[2010/02/10]" reg 10-Feb-09 Test 1 A $10 $10 B $-10 0 10-Feb-10 Test 2 B $10 $10 C $-10 0 end test test -l "date==[2010/02/10]" reg 10-Feb-10 Test 2 B $10 $10 C $-10 0 end test test -l "date>[2010/02/10]" reg 10-Feb-11 Test 3 C $10 $10 D $-10 0 end test test -l "date<[2010/02/10]" reg 10-Feb-09 Test 1 A $10 $10 B $-10 0 end test ��������������������������������������������ledger-3.3.2/test/regress/C9D593B3.test�������������������������������������������������������������0000664�0000000�0000000�00000000571�14411236400�0017316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-16 KFC Expenses:Food $20 Assets:Cash 2012-03-16 KFC Expenses:Food $20 Assets:Cash 2012-03-16 KFC Expenses:Food $20 Assets:Cash 2012-03-16 KFC Expenses:Food $20 Assets:Cash 2012-03-16 KFC Expenses:Food $20 Assets:Cash test payees KFC end test ���������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/CAE63F5C-a.test�����������������������������������������������������������0000664�0000000�0000000�00000000712�14411236400�0017563�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011/03/01 test1 a 4.00 € b 2011/03/02 test2 a 4.00 € b 2011/03/03 test2 a 4.00 € b test reg a 11-Mar-01 test1 a 4.00 € 4.00 € 11-Mar-02 test2 a 4.00 € 8.00 € 11-Mar-03 test2 a 4.00 € 12.00 € end test ������������������������������������������������������ledger-3.3.2/test/regress/CAE63F5C-b.test�����������������������������������������������������������0000664�0000000�0000000�00000000700�14411236400�0017561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012/08/22 Payment Accrued €208.00 {=$1.3109} @ $1.2799 Expenses €4.16 {=$1.2798689} @ $1.2799 Assets $-271.54 Income:Currency Conversion $-6.45 test bal -X $ $272.67 Accrued $-271.54 Assets $5.32 Expenses $-6.45 Income:Currency Conversion -------------------- 0 end test ����������������������������������������������������������������ledger-3.3.2/test/regress/CAE63F5C-c.test�����������������������������������������������������������0000664�0000000�0000000�00000000706�14411236400�0017570�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012/08/22 Payment Accrued €208.00 {=$1.3109} @ $1.2798689 Expenses €4.16 {=$1.2798689} @ $1.2798689 Assets $-271.54 Income:Currency Conversion $-6.45 test bal -X $ $272.67 Accrued $-271.54 Assets $5.32 Expenses $-6.45 Income:Currency Conversion -------------------- 0 end test ����������������������������������������������������������ledger-3.3.2/test/regress/CEECC0B0.test�������������������������������������������������������������0000664�0000000�0000000�00000001016�14411236400�0017350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Opening Balances Assets:Cash 100.00 EUR Equity:Opening balances -100.00 EUR 2012-01-02 * Buy AAA Assets:Investments 1 AAA @ 10.00 EUR Assets:Cash -10.00 EUR 2012-01-03 * Sell AAA Assets:Investments -1 AAA @ 10.00 EUR Assets:Cash 10.00 EUR test equity 2012/01/03 Opening Balances Assets:Cash 100.00 EUR Equity:Opening balances end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/CFE5D8AA.test�������������������������������������������������������������0000664�0000000�0000000�00000000460�14411236400�0017366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ monthly assets:checking $1,000.00 income:work:salary $-1,000.00 ~ monthly ; note assets:checking $1,000.00 income:work:salary $-1,000.00 ~ monthly assets:checking $1,000.00 income:work:salary ~ monthly ; note assets:checking $1,000.00 income:work:salary test reg end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/CMakeLists.txt������������������������������������������������������������0000664�0000000�0000000�00000000042�14411236400�0020141�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������add_ledger_harness_tests(Regress) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/D060256A.test�������������������������������������������������������������0000664�0000000�0000000�00000000600�14411236400�0017251�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly Expenses:Food $500.00 Assets 2009/11/01 Sample Expenses:Food:Dining $20.00 Assets test budget --now=2009/11/01 --end=2009/11/30 $-20.00 $-500.00 $480.00 4% Assets $20.00 $500.00 $-480.00 4% Expenses:Food ------------ ------------ ------------ ----- 0 0 0 0 end test ��������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/D2829FC4.test�������������������������������������������������������������0000664�0000000�0000000�00000010471�14411236400�0017316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly since 2010/01/01 Expenses:Bills:Rent $873.00 Expenses:Household $200.00 Income:Salary -$2491.60 Assets:Bank:Checking ~ biweekly from 2010/02/23 Expenses:Bills:Housecleaning $85.00 Assets:Bank:Checking test reg --forecast 'date<[2011]' --now=2010/06/20 10-Jul-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Jul-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Jul-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Jul-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Jul-04 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jul-04 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Aug-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Aug-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Aug-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Aug-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Jul-18 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Jul-18 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Aug-01 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Aug-01 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Sep-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Sep-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Sep-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Sep-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Aug-15 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Aug-15 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Aug-29 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Aug-29 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Sep-12 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Sep-12 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Oct-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Oct-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Oct-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Oct-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Sep-26 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Sep-26 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Oct-10 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Oct-10 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Nov-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Nov-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Nov-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Nov-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Oct-24 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Oct-24 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Nov-07 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Nov-07 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Dec-01 Forecast transaction Expenses:Bills:Rent $873.00 $873.00 10-Dec-01 Forecast transaction Expenses:Household $200.00 $1073.00 10-Dec-01 Forecast transaction Income:Salary $-2491.60 $-1418.60 10-Dec-01 Forecast transaction Assets:Bank:Checking $1418.60 0 10-Nov-21 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Nov-21 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Dec-05 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Dec-05 Forecast transaction Assets:Bank:Checking $-85.00 0 10-Dec-19 Forecast transaction Ex:Bills:Housecleaning $85.00 $85.00 10-Dec-19 Forecast transaction Assets:Bank:Checking $-85.00 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/D51BFF74.test�������������������������������������������������������������0000664�0000000�0000000�00000001076�14411236400�0017334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-16 KFC Expenses:Food $-20 Assets:Cash 2012-03-16 KFC Expenses:Food $- 20 Assets:Cash 2012-03-16 KFC Expenses:Food -$20 Assets:Cash 2012-03-16 KFC Expenses:Food - $20 Assets:Cash test reg -> 1 __ERROR__ While parsing file "$FILE", line 6: While parsing posting: Expenses:Food $- 20 ^^^^^ Error: No quantity specified for amount end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/D943AE0F.test�������������������������������������������������������������0000664�0000000�0000000�00000000700�14411236400�0017322�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 EUR 2008/04/15 * Paid expenses back from cie. Expenses:Cie-Reimbursements 2000 CAD @ 1.10 EUR Assets:Checking P 2008/04/20 00:00:00 CAD 1.20 EUR test reg -V --now=2008/04/20 08-Apr-15 Paid expenses back .. Exp:Cie-Reimbursements 2200.00 EUR 2200.00 EUR Assets:Checking -2200.00 EUR 0 08-Apr-20 Commodities revalued <Revalued> 200.00 EUR 200.00 EUR end test ����������������������������������������������������������������ledger-3.3.2/test/regress/D9C8EB08.test�������������������������������������������������������������0000664�0000000�0000000�00000000452�14411236400�0017335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: Using ! erroneously in a data file causes a segfault ! Assets:Cash 2008/01/01 January Expenses:Books $10.00 Assets:Cash !end test bal -> 1 __ERROR__ While parsing file "$FILE", line 9: Error: 'end' or 'end apply' found, but no enclosing 'apply' directive end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/DB490507.test�������������������������������������������������������������0000664�0000000�0000000�00000001643�14411236400�0017270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2001/11/07=2001/11/04 * Autoroutes du Sud de la France Dépense:Vacances:Voyage ; 14F Tlse-Montauban, 8F Montauban-Caussade, 8F Caussade-Montauban, 14F Montauban-Tlse Actif:Courant:BnpCc -6,71 € 2008/01/20 * La Poste Equity Actif:Courant:LaPosteLivretA 10,00 € 2008/01/20 * La Poste Revenu:Invest:Exonéré Actif:Courant:LaPosteLivretA 25,24 € = 35,24 € test print --decimal-comma --columns=999 2001/11/07=2001/11/04 * Autoroutes du Sud de la France Dépense:Vacances:Voyage ; 14F Tlse-Montauban, 8F Montauban-Caussade, 8F Caussade-Montauban, 14F Montauban-Tlse Actif:Courant:BnpCc -6,71 € 2008/01/20 * La Poste Equity Actif:Courant:LaPosteLivretA 10,00 € 2008/01/20 * La Poste Revenu:Invest:Exonéré Actif:Courant:LaPosteLivretA 25,24 € = 35,24 € end test ���������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/DDB54BB8.test�������������������������������������������������������������0000664�0000000�0000000�00000000621�14411236400�0017343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly Aufwand:Bargeld 0,30€ Aktiva:Bank:Girokonto -0,40€ test bal -> 1 __ERROR__ While parsing file "$FILE", line 3: Unbalanced remainder is: -0,10€ Amount to balance against: 0,30€ While parsing periodic transaction: > ~ Monthly > Aufwand:Bargeld 0,30€ > Aktiva:Bank:Girokonto -0,40€ Error: Transaction does not balance end test ���������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/DE17CCF1.test�������������������������������������������������������������0000664�0000000�0000000�00000003276�14411236400�0017353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ --date-format %Y-%m-%d 2014-06-30 Uncleared U:U 10.00 EUR ! U:P 10.00 EUR * U:C 10.00 EUR Equity -30.00 EUR 2014-06-30 ! Pending P:U 10.00 EUR ! P:P 10.00 EUR * P:C 10.00 EUR Equity -30.00 EUR 2014-06-30 * Cleared C:C 10.00 EUR ! C:P 10.00 EUR * C:P 10.00 EUR Equity -30.00 EUR test reg u: --uncleared 2014-06-30 Uncleared U:U 10.00 EUR 10.00 EUR U:P 10.00 EUR 20.00 EUR end test test reg u: --pending 2014-06-30 Uncleared U:P 10.00 EUR 10.00 EUR end test test reg u: --cleared 2014-06-30 Uncleared U:C 10.00 EUR 10.00 EUR end test test reg p: --uncleared 2014-06-30 Pending P:U 10.00 EUR 10.00 EUR P:P 10.00 EUR 20.00 EUR end test test reg p: --pending 2014-06-30 Pending P:U 10.00 EUR 10.00 EUR P:P 10.00 EUR 20.00 EUR end test test reg p: --cleared 2014-06-30 Pending P:C 10.00 EUR 10.00 EUR end test test reg c: --uncleared 2014-06-30 Cleared C:P 10.00 EUR 10.00 EUR end test test reg c: --pending 2014-06-30 Cleared C:P 10.00 EUR 10.00 EUR end test test reg c: --cleared 2014-06-30 Cleared C:C 10.00 EUR 10.00 EUR C:P 10.00 EUR 20.00 EUR end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/E2E479BC.test�������������������������������������������������������������0000664�0000000�0000000�00000000704�14411236400�0017333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: ledger used to show multiple "Income:Unknown" in this ; case in the past, which it shouldn't. 2009/01/01 Sample Expenses:Alpha 10 A Expenses:Beta 10 B Expenses:Gamma 10 C Income:Unknown test print 2009/01/01 Sample Expenses:Alpha 10 A Expenses:Beta 10 B Expenses:Gamma 10 C Income:Unknown end test ������������������������������������������������������������ledger-3.3.2/test/regress/E4C9A8EA.test�������������������������������������������������������������0000664�0000000�0000000�00000002511�14411236400�0017350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2007/12/31 * Cost basis for: RED HAT INC RHT Assets:Investments:RBC-Broker:Account-RSP 4 RHT Equity:Opening-Balances:Cost -689.87 CAD 2008/01/03=2007/12/28 * Sell -- RHT -- RED HAT INC CA TAUX DE CHANGE .96590 Assets:Investments:RBC-Broker:Account-RSP -4.00 RHT @ 21.14 CAD Expenses:Financial:Commissions 9.95 USD @ 0.96590 CAD Assets:Investments:RBC-Broker:Account-RSP 72.06 CAD Expenses:Financial:Fees test reg 07-Dec-31 Cost basis for: RED.. As:In:RBC-:Account-RSP 4.00 RHT 4.00 RHT Eq:Opening-Balanc:Cost -689.87 CAD -689.87 CAD 4.00 RHT 08-Jan-03 Sell -- RHT -- RED .. As:In:RBC-:Account-RSP -4.00 RHT -689.87 CAD Ex:Financi:Commissions 9.95 USD -689.87 CAD 9.95 USD As:In:RBC-:Account-RSP 72.06 CAD -617.81 CAD 9.95 USD Expense:Financial:Fees 2.89 CAD -614.92 CAD 9.95 USD end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/E627C594.test�������������������������������������������������������������0000664�0000000�0000000�00000001513�14411236400�0017276�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ Monthly Expenses:Food $500.00 Assets 2009/11/01 Sample Expenses:Food:Dining $20.00 Assets test reg --forecast-while="d<[2010/03/01]" --now=2009/11/01 09-Nov-01 Sample Expenses:Food:Dining $20.00 $20.00 Assets $-20.00 0 09-Dec-01 Forecast transaction Expenses:Food $500.00 $500.00 09-Dec-01 Forecast transaction Assets $-500.00 0 10-Jan-01 Forecast transaction Expenses:Food $500.00 $500.00 10-Jan-01 Forecast transaction Assets $-500.00 0 10-Feb-01 Forecast transaction Expenses:Food $500.00 $500.00 10-Feb-01 Forecast transaction Assets $-500.00 0 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/E9F130C5.test�������������������������������������������������������������0000664�0000000�0000000�00000001011�14411236400�0017276�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2008/01/01 income assets:bank:checking $1 income:salary 2008/06/01 gift assets:bank:checking $1 income:gifts 2008/06/02 save assets:bank:saving $1 assets:bank:checking 2008/06/03 * eat & shop expenses:food $1 expenses:supplies $1 assets:cash 2008/12/31 * pay off liabilities:debts $1 assets:bank:checking test bal as $-1 assets $1 bank:saving $-2 cash -------------------- $-1 end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/EA18D948.test�������������������������������������������������������������0000664�0000000�0000000�00000000666�14411236400�0017325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-01-01 * Buy AAA A 1 AAA @ 1.00 EUR B -1.00 EUR 2012-02-01 * Buy AAA A 1 AAA @ 2.00 EUR B -2.00 EUR test reg A -V -A 12-Jan-01 Buy AAA A 1.00 EUR 1.00 EUR 12-Feb-01 Commodities revalued <Revalued> 1.00 EUR 0 12-Feb-01 Buy AAA A 2.00 EUR 2.00 EUR end test ��������������������������������������������������������������������������ledger-3.3.2/test/regress/F06D5554.test�������������������������������������������������������������0000664�0000000�0000000�00000103412�14411236400�0017271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2011/04/01 serveraxis.com Expenses:Computer:Internet $15.00 Expenses:Computer:Internet $1.10 Liabilities:MasterCard 2011/04/05 Pennsylvania toll booth Expenses:Auto:Fees $13.00 Expenses:Cash 2011/04/05 iTunes Expenses:Music $1.29 Expenses:Taxes:Sales $0.09 Liabilities:MasterCard $-1.38 2011/04/19 iTunes Expenses:Computer:Software $4.99 Expenses:Taxes:Sales $0.35 Liabilities:MasterCard $-5.34 2011/04/24 iTunes Expenses:Movies $1.99 Expenses:Movies $2.99 Expenses:Taxes:Sales $0.35 Liabilities:MasterCard $-5.33 2011/04/29 iTunes Expenses:Computer:Movies $0.99 Expenses:Taxes:Sales $0.07 Liabilities:MasterCard $-1.06 2011/05/01 serveraxis.com Expenses:Computer:Internet $15.00 Expenses:Computer:Internet $1.10 Liabilities:MasterCard 2011/05/18 iTunes Expenses:Computer:Software $6.99 Expenses:Taxes:Sales $0.49 Liabilities:MasterCard $-7.48 2011/05/20 DynDNS.com Expenses:Computer:Internet $15.00 Liabilities:MasterCard 2011/05/20 DynDNS.com Expenses:Computer:Internet $15.00 Liabilities:MasterCard 2011/05/27 iTunes Expenses:Movies $1.99 Expenses:Movies $1.99 Expenses:Movies $1.99 Expenses:Taxes:Sales $0.42 Liabilities:MasterCard $-6.39 2011/05/26 Valero Expenses:Auto:Gas $26.79 Liabilities:MasterCard 2011/05/26 Starbucks Expenses:Food $2.20 Expenses:Taxes:Sales $0.15 Liabilities:MasterCard $-2.35 2011/05/26 La Mex Expenses:Food $17.70 Expenses:Taxes:Sales $1.11 Expenses:Tips $3.00 Liabilities:MasterCard $-21.81 2011/05/27 Leaves N Beans Expenses:Food:Dining $20.98 Expenses:Taxes:Sales $1.63 Expenses:Tips $2.00 Liabilities:MasterCard $-24.61 2011/05/27 Wal*Mart Expenses:Home:Supplies $7.97 Expenses:Food:Grocery $3.25 Expenses:Food:Grocery $3.18 Expenses:Food:Grocery $3.18 Expenses:Food:Grocery $2.98 Expenses:Food:Grocery $1.98 Expenses:Food:Grocery $3.98 Expenses:Food:Grocery $3.58 Expenses:Food:Grocery $3.58 Expenses:Food:Grocery $1.58 Expenses:Food:Grocery $1.88 Expenses:Food:Grocery $2.50 Expenses:Food:Grocery $1.26 Expenses:Food:Grocery $2.62 Expenses:Food:Grocery $3.48 Expenses:Home:Supplies $1.37 Expenses:Home:Supplies $2.92 Expenses:Beauty $3.38 Expenses:Beauty $0.97 Expenses:Beauty $4.64 Expenses:Beauty $1.97 Expenses:Beauty $1.97 Expenses:Beauty $5.98 Expenses:Home:Supplies $9.98 Expenses:Bedding $4.00 Expenses:Bedding $4.00 Expenses:Home:Supplies $2.88 Expenses:Home:Supplies $2.88 Expenses:Home:Supplies $2.88 Expenses:Home:Supplies $2.88 Expenses:Clothing $2.96 Expenses:Supplies $0.84 Expenses:Food:Grocery $1.38 Expenses:Food:Grocery $1.38 Expenses:Food:Grocery $2.32 Expenses:Food:Grocery $2.00 Expenses:Food:Grocery $2.98 Expenses:Food:Grocery $3.00 Expenses:Food:Grocery $2.14 Expenses:Food:Grocery $2.14 Expenses:Food:Grocery $2.50 Expenses:Food:Grocery $2.50 Expenses:Food:Grocery $3.48 Expenses:Home:Supplies $1.17 Expenses:Supplies $3.00 Expenses:Bedding $34.88 Expenses:Home $6.00 Expenses:Home $6.00 Expenses:Home:Supplies $3.97 Expenses:Food:Grocery $0.78 Expenses:Food:Grocery $0.78 Expenses:Food:Grocery $0.78 Expenses:Food:Grocery $0.78 Expenses:Home $4.00 Expenses:Home $4.00 Expenses:Home $10.87 Expenses:Home $4.00 Expenses:Bedding $65.96 Expenses:Taxes:Sales $16.89 Expenses:Taxes:Sales $0.65 Liabilities:MasterCard $-293.83 2011/05/27 Asia Grill Expenses:Food:Dining $28.63 Expenses:Tips $4.00 Liabilities:MasterCard $-32.63 2011/05/28 Shell Expenses:Auto:Gas $43.41 Liabilities:MasterCard 2011/05/28 Sears Expenses:Home $1,728.96 Expenses:Taxes:Sales $136.87 Liabilities:MasterCard $-1,865.83 2011/05/28 Sears Expenses:Home $99.61 Expenses:Taxes:Sales $8.22 Liabilities:MasterCard $-107.83 2011/05/28 Buffalo Wild Wings Expenses:Food:Dining $22.98 Expenses:Tips $2.35 Expenses:Taxes:Sales $3.50 Liabilities:MasterCard $-28.83 2011/05/28 Cold Stone Creamery Expenses:Food:Dining $5.73 Expenses:Tips $0.50 Liabilities:MasterCard $-6.23 2011/05/29 Hy Vee Expenses:Supplies $2.00 Expenses:Supplies $7.99 Expenses:Supplies $7.99 Expenses:Food:Grocery $157.64 Expenses:Taxes:Sales $5.74 Liabilities:MasterCard $-181.36 2011/05/30 Allied movers, Fidel & Manny Expenses:Tips $97.00 Expenses:Cash 2011/05/30 Starbucks Expenses:Food:Dining $6.90 Expenses:Taxes:Sales $0.71 Liabilities:MasterCard $-7.61 2011/05/31 Wal*Mart Expenses:Home $108.13 Expenses:Taxes:Sales $8.65 Liabilities:MasterCard $-116.78 test reg -p "apr 2011" Expenses 11-Apr-01 serveraxis.com Expe:Computer:Internet $15.00 $15.00 Expe:Computer:Internet $1.10 $16.10 11-Apr-05 Pennsylvania toll b.. Expenses:Auto:Fees $13.00 $29.10 Expenses:Cash $-13.00 $16.10 11-Apr-05 iTunes Expenses:Music $1.29 $17.39 Expenses:Taxes:Sales $0.09 $17.48 11-Apr-19 iTunes Expe:Computer:Software $4.99 $22.47 Expenses:Taxes:Sales $0.35 $22.82 11-Apr-24 iTunes Expenses:Movies $1.99 $24.81 Expenses:Movies $2.99 $27.80 Expenses:Taxes:Sales $0.35 $28.15 11-Apr-29 iTunes Expens:Computer:Movies $0.99 $29.14 Expenses:Taxes:Sales $0.07 $29.21 end test test reg -p "apr 2011" Expenses --monthly 11-Apr-01 - 11-Apr-30 Expenses:Auto:Fees $13.00 $13.00 Expenses:Cash $-13.00 0 Expe:Computer:Internet $16.10 $16.10 Expens:Computer:Movies $0.99 $17.09 Expe:Computer:Software $4.99 $22.08 Expenses:Movies $4.98 $27.06 Expenses:Music $1.29 $28.35 Expenses:Taxes:Sales $0.86 $29.21 end test test reg -p "apr 2011" Expenses --monthly --exact 11-Apr-01 - 11-Apr-29 Expenses:Auto:Fees $13.00 $13.00 Expenses:Cash $-13.00 0 Expe:Computer:Internet $16.10 $16.10 Expens:Computer:Movies $0.99 $17.09 Expe:Computer:Software $4.99 $22.08 Expenses:Movies $4.98 $27.06 Expenses:Music $1.29 $28.35 Expenses:Taxes:Sales $0.86 $29.21 end test test reg -p "apr 2011" Expenses --weekly 11-Apr-01 - 11-Apr-02 Expe:Computer:Internet $16.10 $16.10 11-Apr-03 - 11-Apr-09 Expenses:Auto:Fees $13.00 $29.10 Expenses:Cash $-13.00 $16.10 Expenses:Music $1.29 $17.39 Expenses:Taxes:Sales $0.09 $17.48 11-Apr-17 - 11-Apr-23 Expe:Computer:Software $4.99 $22.47 Expenses:Taxes:Sales $0.35 $22.82 11-Apr-24 - 11-Apr-30 Expens:Computer:Movies $0.99 $23.81 Expenses:Movies $4.98 $28.79 Expenses:Taxes:Sales $0.42 $29.21 end test test reg -p "apr 2011" Expenses --weekly --exact 11-Apr-01 - 11-Apr-01 Expe:Computer:Internet $16.10 $16.10 11-Apr-05 - 11-Apr-05 Expenses:Auto:Fees $13.00 $29.10 Expenses:Cash $-13.00 $16.10 Expenses:Music $1.29 $17.39 Expenses:Taxes:Sales $0.09 $17.48 11-Apr-19 - 11-Apr-19 Expe:Computer:Software $4.99 $22.47 Expenses:Taxes:Sales $0.35 $22.82 11-Apr-24 - 11-Apr-29 Expens:Computer:Movies $0.99 $23.81 Expenses:Movies $4.98 $28.79 Expenses:Taxes:Sales $0.42 $29.21 end test test reg -p "apr 2011" Expenses --weekly --empty 11-Apr-01 - 11-Apr-02 Expe:Computer:Internet $16.10 $16.10 11-Apr-03 - 11-Apr-09 Expenses:Auto:Fees $13.00 $29.10 Expenses:Cash $-13.00 $16.10 Expenses:Music $1.29 $17.39 Expenses:Taxes:Sales $0.09 $17.48 11-Apr-10 - 11-Apr-16 <None> 0 $17.48 11-Apr-17 - 11-Apr-23 Expe:Computer:Software $4.99 $22.47 Expenses:Taxes:Sales $0.35 $22.82 11-Apr-24 - 11-Apr-30 Expens:Computer:Movies $0.99 $23.81 Expenses:Movies $4.98 $28.79 Expenses:Taxes:Sales $0.42 $29.21 end test test reg -p "apr 2011" Expenses --weekly --empty --exact 11-Apr-01 - 11-Apr-01 Expe:Computer:Internet $16.10 $16.10 11-Apr-05 - 11-Apr-05 Expenses:Auto:Fees $13.00 $29.10 Expenses:Cash $-13.00 $16.10 Expenses:Music $1.29 $17.39 Expenses:Taxes:Sales $0.09 $17.48 11-Apr-16 - 11-Apr-16 <None> 0 $17.48 11-Apr-19 - 11-Apr-19 Expe:Computer:Software $4.99 $22.47 Expenses:Taxes:Sales $0.35 $22.82 11-Apr-24 - 11-Apr-29 Expens:Computer:Movies $0.99 $23.81 Expenses:Movies $4.98 $28.79 Expenses:Taxes:Sales $0.42 $29.21 end test test reg -p "may 2011" 11-May-01 serveraxis.com Expe:Computer:Internet $15.00 $15.00 Expe:Computer:Internet $1.10 $16.10 Liabilities:MasterCard $-16.10 0 11-May-18 iTunes Expe:Computer:Software $6.99 $6.99 Expenses:Taxes:Sales $0.49 $7.48 Liabilities:MasterCard $-7.48 0 11-May-20 DynDNS.com Expe:Computer:Internet $15.00 $15.00 Liabilities:MasterCard $-15.00 0 11-May-20 DynDNS.com Expe:Computer:Internet $15.00 $15.00 Liabilities:MasterCard $-15.00 0 11-May-27 iTunes Expenses:Movies $1.99 $1.99 Expenses:Movies $1.99 $3.98 Expenses:Movies $1.99 $5.97 Expenses:Taxes:Sales $0.42 $6.39 Liabilities:MasterCard $-6.39 0 11-May-26 Valero Expenses:Auto:Gas $26.79 $26.79 Liabilities:MasterCard $-26.79 0 11-May-26 Starbucks Expenses:Food $2.20 $2.20 Expenses:Taxes:Sales $0.15 $2.35 Liabilities:MasterCard $-2.35 0 11-May-26 La Mex Expenses:Food $17.70 $17.70 Expenses:Taxes:Sales $1.11 $18.81 Expenses:Tips $3.00 $21.81 Liabilities:MasterCard $-21.81 0 11-May-27 Leaves N Beans Expenses:Food:Dining $20.98 $20.98 Expenses:Taxes:Sales $1.63 $22.61 Expenses:Tips $2.00 $24.61 Liabilities:MasterCard $-24.61 0 11-May-27 Wal*Mart Expenses:Home:Supplies $7.97 $7.97 Expenses:Food:Grocery $3.25 $11.22 Expenses:Food:Grocery $3.18 $14.40 Expenses:Food:Grocery $3.18 $17.58 Expenses:Food:Grocery $2.98 $20.56 Expenses:Food:Grocery $1.98 $22.54 Expenses:Food:Grocery $3.98 $26.52 Expenses:Food:Grocery $3.58 $30.10 Expenses:Food:Grocery $3.58 $33.68 Expenses:Food:Grocery $1.58 $35.26 Expenses:Food:Grocery $1.88 $37.14 Expenses:Food:Grocery $2.50 $39.64 Expenses:Food:Grocery $1.26 $40.90 Expenses:Food:Grocery $2.62 $43.52 Expenses:Food:Grocery $3.48 $47.00 Expenses:Home:Supplies $1.37 $48.37 Expenses:Home:Supplies $2.92 $51.29 Expenses:Beauty $3.38 $54.67 Expenses:Beauty $0.97 $55.64 Expenses:Beauty $4.64 $60.28 Expenses:Beauty $1.97 $62.25 Expenses:Beauty $1.97 $64.22 Expenses:Beauty $5.98 $70.20 Expenses:Home:Supplies $9.98 $80.18 Expenses:Bedding $4.00 $84.18 Expenses:Bedding $4.00 $88.18 Expenses:Home:Supplies $2.88 $91.06 Expenses:Home:Supplies $2.88 $93.94 Expenses:Home:Supplies $2.88 $96.82 Expenses:Home:Supplies $2.88 $99.70 Expenses:Clothing $2.96 $102.66 Expenses:Supplies $0.84 $103.50 Expenses:Food:Grocery $1.38 $104.88 Expenses:Food:Grocery $1.38 $106.26 Expenses:Food:Grocery $2.32 $108.58 Expenses:Food:Grocery $2.00 $110.58 Expenses:Food:Grocery $2.98 $113.56 Expenses:Food:Grocery $3.00 $116.56 Expenses:Food:Grocery $2.14 $118.70 Expenses:Food:Grocery $2.14 $120.84 Expenses:Food:Grocery $2.50 $123.34 Expenses:Food:Grocery $2.50 $125.84 Expenses:Food:Grocery $3.48 $129.32 Expenses:Home:Supplies $1.17 $130.49 Expenses:Supplies $3.00 $133.49 Expenses:Bedding $34.88 $168.37 Expenses:Home $6.00 $174.37 Expenses:Home $6.00 $180.37 Expenses:Home:Supplies $3.97 $184.34 Expenses:Food:Grocery $0.78 $185.12 Expenses:Food:Grocery $0.78 $185.90 Expenses:Food:Grocery $0.78 $186.68 Expenses:Food:Grocery $0.78 $187.46 Expenses:Home $4.00 $191.46 Expenses:Home $4.00 $195.46 Expenses:Home $10.87 $206.33 Expenses:Home $4.00 $210.33 Expenses:Bedding $65.96 $276.29 Expenses:Taxes:Sales $16.89 $293.18 Expenses:Taxes:Sales $0.65 $293.83 Liabilities:MasterCard $-293.83 0 11-May-27 Asia Grill Expenses:Food:Dining $28.63 $28.63 Expenses:Tips $4.00 $32.63 Liabilities:MasterCard $-32.63 0 11-May-28 Shell Expenses:Auto:Gas $43.41 $43.41 Liabilities:MasterCard $-43.41 0 11-May-28 Sears Expenses:Home $1,728.96 $1,728.96 Expenses:Taxes:Sales $136.87 $1,865.83 Liabilities:MasterCard $-1,865.83 0 11-May-28 Sears Expenses:Home $99.61 $99.61 Expenses:Taxes:Sales $8.22 $107.83 Liabilities:MasterCard $-107.83 0 11-May-28 Buffalo Wild Wings Expenses:Food:Dining $22.98 $22.98 Expenses:Tips $2.35 $25.33 Expenses:Taxes:Sales $3.50 $28.83 Liabilities:MasterCard $-28.83 0 11-May-28 Cold Stone Creamery Expenses:Food:Dining $5.73 $5.73 Expenses:Tips $0.50 $6.23 Liabilities:MasterCard $-6.23 0 11-May-29 Hy Vee Expenses:Supplies $2.00 $2.00 Expenses:Supplies $7.99 $9.99 Expenses:Supplies $7.99 $17.98 Expenses:Food:Grocery $157.64 $175.62 Expenses:Taxes:Sales $5.74 $181.36 Liabilities:MasterCard $-181.36 0 11-May-30 Allied movers, Fide.. Expenses:Tips $97.00 $97.00 Expenses:Cash $-97.00 0 11-May-30 Starbucks Expenses:Food:Dining $6.90 $6.90 Expenses:Taxes:Sales $0.71 $7.61 Liabilities:MasterCard $-7.61 0 11-May-31 Wal*Mart Expenses:Home $108.13 $108.13 Expenses:Taxes:Sales $8.65 $116.78 Liabilities:MasterCard $-116.78 0 end test test reg -p "may 2011" --monthly 11-May-01 - 11-May-31 Expenses:Auto:Gas $70.20 $70.20 Expenses:Beauty $18.91 $89.11 Expenses:Bedding $108.84 $197.95 Expenses:Cash $-97.00 $100.95 Expenses:Clothing $2.96 $103.91 Expe:Computer:Internet $46.10 $150.01 Expe:Computer:Software $6.99 $157.00 Expenses:Food $19.90 $176.90 Expenses:Food:Dining $85.22 $262.12 Expenses:Food:Grocery $225.61 $487.73 Expenses:Home $1,971.57 $2,459.30 Expenses:Home:Supplies $38.90 $2,498.20 Expenses:Movies $5.97 $2,504.17 Expenses:Supplies $21.82 $2,525.99 Expenses:Taxes:Sales $185.03 $2,711.02 Expenses:Tips $108.85 $2,819.87 Liabilities:MasterCard $-2,819.87 0 end test test reg -p "may 2011" --weekly 11-May-01 - 11-May-07 Expe:Computer:Internet $16.10 $16.10 Liabilities:MasterCard $-16.10 0 11-May-15 - 11-May-21 Expe:Computer:Internet $30.00 $30.00 Expe:Computer:Software $6.99 $36.99 Expenses:Taxes:Sales $0.49 $37.48 Liabilities:MasterCard $-37.48 0 11-May-22 - 11-May-28 Expenses:Auto:Gas $70.20 $70.20 Expenses:Beauty $18.91 $89.11 Expenses:Bedding $108.84 $197.95 Expenses:Clothing $2.96 $200.91 Expenses:Food $19.90 $220.81 Expenses:Food:Dining $78.32 $299.13 Expenses:Food:Grocery $67.97 $367.10 Expenses:Home $1,863.44 $2,230.54 Expenses:Home:Supplies $38.90 $2,269.44 Expenses:Movies $5.97 $2,275.41 Expenses:Supplies $3.84 $2,279.25 Expenses:Taxes:Sales $169.44 $2,448.69 Expenses:Tips $11.85 $2,460.54 Liabilities:MasterCard $-2,460.54 0 11-May-29 - 11-May-31 Expenses:Cash $-97.00 $-97.00 Expenses:Food:Dining $6.90 $-90.10 Expenses:Food:Grocery $157.64 $67.54 Expenses:Home $108.13 $175.67 Expenses:Supplies $17.98 $193.65 Expenses:Taxes:Sales $15.10 $208.75 Expenses:Tips $97.00 $305.75 Liabilities:MasterCard $-305.75 0 end test test reg -p "may 2011" --weekly --exact 11-May-01 - 11-May-01 Expe:Computer:Internet $16.10 $16.10 Liabilities:MasterCard $-16.10 0 11-May-18 - 11-May-20 Expe:Computer:Internet $30.00 $30.00 Expe:Computer:Software $6.99 $36.99 Expenses:Taxes:Sales $0.49 $37.48 Liabilities:MasterCard $-37.48 0 11-May-26 - 11-May-28 Expenses:Auto:Gas $70.20 $70.20 Expenses:Beauty $18.91 $89.11 Expenses:Bedding $108.84 $197.95 Expenses:Clothing $2.96 $200.91 Expenses:Food $19.90 $220.81 Expenses:Food:Dining $78.32 $299.13 Expenses:Food:Grocery $67.97 $367.10 Expenses:Home $1,863.44 $2,230.54 Expenses:Home:Supplies $38.90 $2,269.44 Expenses:Movies $5.97 $2,275.41 Expenses:Supplies $3.84 $2,279.25 Expenses:Taxes:Sales $169.44 $2,448.69 Expenses:Tips $11.85 $2,460.54 Liabilities:MasterCard $-2,460.54 0 11-May-29 - 11-May-31 Expenses:Cash $-97.00 $-97.00 Expenses:Food:Dining $6.90 $-90.10 Expenses:Food:Grocery $157.64 $67.54 Expenses:Home $108.13 $175.67 Expenses:Supplies $17.98 $193.65 Expenses:Taxes:Sales $15.10 $208.75 Expenses:Tips $97.00 $305.75 Liabilities:MasterCard $-305.75 0 end test test reg -p "may 2011" --weekly --empty 11-May-01 - 11-May-07 Expe:Computer:Internet $16.10 $16.10 Liabilities:MasterCard $-16.10 0 11-May-08 - 11-May-14 <None> 0 0 11-May-15 - 11-May-21 Expe:Computer:Internet $30.00 $30.00 Expe:Computer:Software $6.99 $36.99 Expenses:Taxes:Sales $0.49 $37.48 Liabilities:MasterCard $-37.48 0 11-May-22 - 11-May-28 Expenses:Auto:Gas $70.20 $70.20 Expenses:Beauty $18.91 $89.11 Expenses:Bedding $108.84 $197.95 Expenses:Clothing $2.96 $200.91 Expenses:Food $19.90 $220.81 Expenses:Food:Dining $78.32 $299.13 Expenses:Food:Grocery $67.97 $367.10 Expenses:Home $1,863.44 $2,230.54 Expenses:Home:Supplies $38.90 $2,269.44 Expenses:Movies $5.97 $2,275.41 Expenses:Supplies $3.84 $2,279.25 Expenses:Taxes:Sales $169.44 $2,448.69 Expenses:Tips $11.85 $2,460.54 Liabilities:MasterCard $-2,460.54 0 11-May-29 - 11-May-31 Expenses:Cash $-97.00 $-97.00 Expenses:Food:Dining $6.90 $-90.10 Expenses:Food:Grocery $157.64 $67.54 Expenses:Home $108.13 $175.67 Expenses:Supplies $17.98 $193.65 Expenses:Taxes:Sales $15.10 $208.75 Expenses:Tips $97.00 $305.75 Liabilities:MasterCard $-305.75 0 end test test reg -p "may 2011" --weekly --empty --exact 11-May-01 - 11-May-01 Expe:Computer:Internet $16.10 $16.10 Liabilities:MasterCard $-16.10 0 11-May-14 - 11-May-14 <None> 0 0 11-May-18 - 11-May-20 Expe:Computer:Internet $30.00 $30.00 Expe:Computer:Software $6.99 $36.99 Expenses:Taxes:Sales $0.49 $37.48 Liabilities:MasterCard $-37.48 0 11-May-26 - 11-May-28 Expenses:Auto:Gas $70.20 $70.20 Expenses:Beauty $18.91 $89.11 Expenses:Bedding $108.84 $197.95 Expenses:Clothing $2.96 $200.91 Expenses:Food $19.90 $220.81 Expenses:Food:Dining $78.32 $299.13 Expenses:Food:Grocery $67.97 $367.10 Expenses:Home $1,863.44 $2,230.54 Expenses:Home:Supplies $38.90 $2,269.44 Expenses:Movies $5.97 $2,275.41 Expenses:Supplies $3.84 $2,279.25 Expenses:Taxes:Sales $169.44 $2,448.69 Expenses:Tips $11.85 $2,460.54 Liabilities:MasterCard $-2,460.54 0 11-May-29 - 11-May-31 Expenses:Cash $-97.00 $-97.00 Expenses:Food:Dining $6.90 $-90.10 Expenses:Food:Grocery $157.64 $67.54 Expenses:Home $108.13 $175.67 Expenses:Supplies $17.98 $193.65 Expenses:Taxes:Sales $15.10 $208.75 Expenses:Tips $97.00 $305.75 Liabilities:MasterCard $-305.75 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/F524E251.test�������������������������������������������������������������0000664�0000000�0000000�00000002076�14411236400�0017270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: ledger -f doc/sample.dat -n reg' shows $0.00 on first post = /^Expenses:Books/ (Liabilities:Taxes) -0.10 ~ Monthly Assets:Bank:Checking $500.00 Income:Salary 2004/05/01 * Checking balance Assets:Bank:Checking $1,000.00 Equity:Opening Balances 2004/05/01 * Investment balance Assets:Brokerage 50 AAPL @ $30.00 Equity:Opening Balances 2004/05/14 * Pay day Assets:Bank:Checking $500.00 Income:Salary 2004/05/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard 2004/05/27 (100) Credit card company Liabilities:MasterCard $20.00 Assets:Bank:Checking test -n reg 04-May-01 Investment balance <Total> $-1,500.00 50 AAPL $-1,500.00 50 AAPL 04-May-27 Book Store <Total> $-2.00 $-1,502.00 50 AAPL end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/F559EC12.test�������������������������������������������������������������0000664�0000000�0000000�00000002201�14411236400�0017304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test format "%-12(scrub(amount))" --- Context is first posting of the following transaction --- 2004/05/27 Book Store ; This note applies to all postings. :SecondTag: Expenses:Books 20 BOOK @ $10 ; Metadata: Some Value ; Typed:: $100 + $200 ; :ExampleTag: ; Here follows a note describing the posting. Liabilities:MasterCard $-200.00 --- Input format string --- %-12(scrub(amount)) --- Format elements --- Element: EXPR flags: 0x1 min: 12 max: 0 expr: scrub(amount) --- Formatted string --- "20 BOOK " end test test format "%12(scrub(amount))" --- Context is first posting of the following transaction --- 2004/05/27 Book Store ; This note applies to all postings. :SecondTag: Expenses:Books 20 BOOK @ $10 ; Metadata: Some Value ; Typed:: $100 + $200 ; :ExampleTag: ; Here follows a note describing the posting. Liabilities:MasterCard $-200.00 --- Input format string --- %12(scrub(amount)) --- Format elements --- Element: EXPR flags: 0x0 min: 12 max: 0 expr: scrub(amount) --- Formatted string --- " 20 BOOK" end test �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/FCE11C8D.test�������������������������������������������������������������0000664�0000000�0000000�00000000301�14411236400�0017336�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-03-17 Payee Expenses:Food $20 Assets:Cash test reg --monthly --invert exp 12-Mar-01 - 12-Mar-31 Expenses:Food $-20 $-20 end test �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/FDFBA165.test�������������������������������������������������������������0000664�0000000�0000000�00000001356�14411236400�0017351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test for: automated transactions didn't show up in the balance report = Income:Clients: (Liabilities:Taxes:VAT) ((1,00 / 1,19) * 0,19) 2009/07/27 * Invoice Assets:Bank:Checking €1.190,00 Income:Clients:ACME_Inc test --decimal-comma bal €1.190,00 Assets:Bank:Checking €-1.190,00 Income:Clients:ACME_Inc €-190,00 Liabilities:Taxes:VAT -------------------- €-190,00 end test test --decimal-comma reg 09-Jul-27 Invoice Assets:Bank:Checking €1.190,00 €1.190,00 Incom:Clients:ACME_Inc €-1.190,00 0 (Liabilitie:Taxes:VAT) €-190,00 €-190,00 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/GH520.test����������������������������������������������������������������0000664�0000000�0000000�00000000545�14411236400�0017037�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009-01-01 * Jan 09 A 100.00 EUR B test reg --format '%(trim(""))\n' end test test reg --format '%(trim("a"))\n' a a end test test reg --format '%(trim(" a"))\n' a a end test test reg --format '%(trim("a "))\n' a a end test test reg --format '%(trim(" a "))\n' a a end test test reg --format '%(trim(" aa "))\n' aa aa end test �����������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/commodity_equivalency.test������������������������������������������������0000664�0000000�0000000�00000000476�14411236400�0022726�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������C 1.00000000 BTC = 100000000 sat 2022/10/01 Sell Bank $20000 Wallet -1 BTC test bal $20000 Bank -1.00000000 BTC Wallet -------------------- $20000 -1.00000000 BTC end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/commodity_equivalency_2.test����������������������������������������������0000664�0000000�0000000�00000000476�14411236400�0023147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������C 1.00000000 BTC = 100000000 sat 2022/10/01 Sell Wallet -1 BTC Bank $20000 test bal $20000 Bank -1.00000000 BTC Wallet -------------------- $20000 -1.00000000 BTC end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/equity-unround.test�������������������������������������������������������0000664�0000000�0000000�00000001546�14411236400�0021324�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������commodity EUR note Euro format 1,000.00 EUR 2022/05/04 * Test 1 Assets:Foo 1.0001 EUR Income 2022/05/04 * Test 2 Assets:Bar 0.0002 EUR Income 2022/05/04 * Test 3 Assets:Baz 3 EUR Income test equity ^Assets: --unround 2022/05/04 Opening Balances Assets:Bar 0.0002 EUR Assets:Baz 3.00 EUR Assets:Foo 1.0001 EUR Equity:Opening Balances -4.0003 EUR end test test reg --equity ^Assets: --unround 22-May-04 Opening Balances Assets:Bar 0.0002 EUR 0.0002 EUR Assets:Baz 3.00 EUR 3.0002 EUR Assets:Foo 1.0001 EUR 4.0003 EUR Equit:Opening Balances -4.0003 EUR 0 end test ����������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/error-in-include.dat������������������������������������������������������0000664�0000000�0000000�00000000124�14411236400�0021252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 2014-05-13 * Does not balance A $10.00 B -$11.00 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/error-in-include.test�����������������������������������������������������0000664�0000000�0000000�00000000753�14411236400�0021471�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ !include error-in-include.dat test bal -> 1 __ERROR__ In file included from "$FILE", line 2: While parsing file "$sourcepath/test/regress/error-in-include.dat", line 4: While balancing transaction from "$sourcepath/test/regress/error-in-include.dat", lines 2-4: > 2014-05-13 * Does not balance > A $10.00 > B -$11.00 Unbalanced remainder is: $-1.00 Amount to balance against: $10.00 Error: Transaction does not balance end test ���������������������ledger-3.3.2/test/regress/fix-missing-trans-in-last-budget-period.test������������������������������0000664�0000000�0000000�00000004472�14411236400�0025774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������= ~ ^A [Balance] 1 [Budget:$account] -1 ~ Monthly from 2014/01 to 2014/12/31 [Budget:A] 100.00 USD [Balance] ~ Monthly from 2014/01 to 2014/12/31 [Budget:Z] 100.00 USD [Balance] 2014/10/01 toto0 [Budget:A:B] 0.01 USD [Balance] 2014/11/01 toto1 A:B 51.00 USD Cash 2014/11/02 toto2 A:B 52.00 USD Cash 2014/11/03 toto3 A:B 53.00 USD Cash 2014/11/04 toto4 A:B 54.00 USD Cash 2014/12/08 toto5 A:B 55.00 USD Cash 2014/12/09 toto6 A:B 56.00 USD Cash 2014/12/10 toto7 A:B 57.00 USD Cash 2014/12/11 toto8 A:B 58.00 USD Cash 2014/12/12 toto9 A:B 59.00 USD Cash 2014/12/12 toto9 C 59.00 USD Cash 2015/01/12 toto10 A:B 59.00 USD Cash test reg --budget -b 2014/10 -e 2015/02 --columns 80 --date-format "%Y-%m-%d" reg ^Bu 2014-10-01 Budget transaction [Budget:A] -100.00 USD -100.00 USD 2014-10-01 Budget transaction [Budget:Z] -100.00 USD -200.00 USD 2014-10-01 toto0 [Budget:A] 0.01 USD -199.99 USD 2014-11-01 Budget transaction [Budget:A] -100.00 USD -299.99 USD 2014-11-01 Budget transaction [Budget:Z] -100.00 USD -399.99 USD 2014-11-01 toto1 [Budget:A] -51.00 USD -450.99 USD 2014-11-02 toto2 [Budget:A] -52.00 USD -502.99 USD 2014-11-03 toto3 [Budget:A] -53.00 USD -555.99 USD 2014-11-04 toto4 [Budget:A] -54.00 USD -609.99 USD 2014-12-01 Budget transaction [Budget:A] -100.00 USD -709.99 USD 2014-12-01 Budget transaction [Budget:Z] -100.00 USD -809.99 USD 2014-12-08 toto5 [Budget:A] -55.00 USD -864.99 USD 2014-12-09 toto6 [Budget:A] -56.00 USD -920.99 USD 2014-12-10 toto7 [Budget:A] -57.00 USD -977.99 USD 2014-12-11 toto8 [Budget:A] -58.00 USD -1035.99 USD 2014-12-12 toto9 [Budget:A] -59.00 USD -1094.99 USD 2015-01-12 toto10 [Budget:A] -59.00 USD -1153.99 USD end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/market-group-by.test������������������������������������������������������0000664�0000000�0000000�00000001534�14411236400�0021336�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D 1000.00 EUR 2008/04/15 * Paid expenses back from cie. Expenses:Cie-Reimbursements 2000 CAD @ 1.10 EUR Assets:Checking 2008/04/15 * Paid expenses back from cie. Expenses:Cie-Reimbursements 2000 USD @ 1.10 EUR Assets:Checking P 2008/04/20 00:00:00 CAD 1.20 EUR P 2008/04/20 00:00:00 USD 1.20 EUR test reg -V --group-by commodity CAD 08-Apr-15 Paid expenses back .. Exp:Cie-Reimbursements 2200.00 EUR 2200.00 EUR 08-Apr-20 Commodities revalued <Revalued> 200.00 EUR 2400.00 EUR EUR 08-Apr-15 Paid expenses back .. Assets:Checking -2200.00 EUR -2200.00 EUR 08-Apr-15 Paid expenses back .. Assets:Checking -2200.00 EUR -4400.00 EUR USD 08-Apr-15 Paid expenses back .. Exp:Cie-Reimbursements 2200.00 EUR 2200.00 EUR 08-Apr-20 Commodities revalued <Revalued> 200.00 EUR 2400.00 EUR end test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/total-1.test��������������������������������������������������������������0000664�0000000�0000000�00000001450�14411236400�0017567�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test that calling total does not affect future calls to that function via a ; stale cache entry for the totalled account, because of a change to that same ; account 2017-10-01 * Opening Balance Assets:Current Account $1000.00 Equity:Opening Balances 2017-10-02 * Savings Assets:Savings $100.00 Assets:Current Account assert account("Assets:Current Account").total == $900.00 2017-10-03 * Savings Assets:Savings $100.00 Assets:Current Account assert account("Assets:Current Account").total == $800.00 test bal $1000.00 Assets $800.00 Current Account $200.00 Savings $-1000.00 Equity:Opening Balances -------------------- 0 end test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/total-2.test��������������������������������������������������������������0000664�0000000�0000000�00000001632�14411236400�0017572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Test that calling total does not affect future calls to that function via a ; stale cache entry for the totalled account, because of a change to an ; descendant (Assets:Current:Unallocated) of that account (Assets:Current) 2017-10-01 * Opening entry Assets:Current:Unallocated $2000.00 Equity:Opening Balances 2017-10-02 * Initial savings Assets:Current:Savings $1000.00 Assets:Current:Unallocated assert account("Assets:Current").total == $2000.00 2017-10-03 * Balance Assets:Current:Unallocated $1.00 Equity:Adjustments assert account("Assets:Current").total == $2001.00 test bal $2001.00 Assets:Current $1000.00 Savings $1001.00 Unallocated $-2001.00 Equity $-1.00 Adjustments $-2000.00 Opening Balances -------------------- 0 end test ������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/xact_code.dat�������������������������������������������������������������0000664�0000000�0000000�00000000135�14411236400�0020027�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2012-11-10 (C0-d3) Payee Assets:Checking € -12,45 Expenses:Expenditure �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/xact_code.py��������������������������������������������������������������0000664�0000000�0000000�00000000170�14411236400�0017706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import ledger for post in ledger.read_journal('test/regress/xact_code.dat').query('expenses'): print(post.xact.code) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/regress/xact_code_py.test���������������������������������������������������������0000664�0000000�0000000�00000000065�14411236400�0020750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test python test/regress/xact_code.py C0-d3 end test ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/unit/�����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0014712�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/unit/CMakeLists.txt���������������������������������������������������������������0000664�0000000�0000000�00000001465�14411236400�0017460�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������macro(add_ledger_test _name) target_link_libraries(${_name} libledger) add_test(Ledger${_name} ${PROJECT_BINARY_DIR}/${_name}) set_tests_properties(Ledger${_name} PROPERTIES ENVIRONMENT "TZ=${Ledger_TEST_TIMEZONE}") endmacro(add_ledger_test _name) include_directories(${PROJECT_SOURCE_DIR}/src) if (BUILD_LIBRARY) add_executable(UtilTests t_times.cc t_format.cc) if (HAVE_BOOST_PYTHON) target_link_libraries(UtilTests ${Python_LIBRARIES}) endif() add_ledger_test(UtilTests) add_executable(MathTests t_amount.cc t_commodity.cc t_balance.cc t_expr.cc t_value.cc) set_source_files_properties(t_amount.cc t_value.cc PROPERTIES COMPILE_FLAGS "-Wno-unused-comparison") if (HAVE_BOOST_PYTHON) target_link_libraries(MathTests ${Python_LIBRARIES}) endif() add_ledger_test(MathTests) endif() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/unit/t_amount.cc������������������������������������������������������������������0000664�0000000�0000000�00000121356�14411236400�0017057�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE math #include <boost/test/unit_test.hpp> #include <system.hh> #include "amount.h" #include "commodity.h" #define internalAmount(x) amount_t::exact(x) using namespace ledger; struct amount_fixture { amount_fixture() { times_initialize(); amount_t::initialize(); // Cause the display precision for dollars to be initialized to 2. amount_t x1("$1.00"); BOOST_CHECK(x1); amount_t::stream_fullstrings = true; // make reports from UnitTests accurate } ~amount_fixture() { amount_t::stream_fullstrings = false; amount_t::shutdown(); times_shutdown(); } }; BOOST_FIXTURE_TEST_SUITE(amount, amount_fixture) BOOST_AUTO_TEST_CASE(testParser) { amount_t x0; amount_t x1; amount_t x2; amount_t x3; amount_t x4("123.456"); amount_t x5(x4); amount_t x6(x4); amount_t x7(x4); amount_t x8("$123.45"); amount_t x9(x8); amount_t x10(x8); amount_t x11(x8); amount_t x12("$100"); BOOST_CHECK_EQUAL(amount_t::precision_t(2), x12.commodity().precision()); #ifndef NOT_FOR_PYTHON string buf("$100..."); std::istringstream input(buf); amount_t x13; x13.parse(input); BOOST_CHECK_EQUAL(x12, x13); #endif // NOT_FOR_PYTHON amount_t x14; BOOST_CHECK_THROW(x14.parse("DM"), amount_error); amount_t x15("$1.000.000,00"); // parsing this switches us to European amount_t x16("$2000"); BOOST_CHECK_EQUAL(string("$2.000,00"), x16.to_string()); x16.parse("$2000,00"); BOOST_CHECK_EQUAL(string("$2.000,00"), x16.to_string()); // Since use of a decimal-comma is an additive quality, we must switch back // to decimal-period manually. x15.commodity().drop_flags(COMMODITY_STYLE_DECIMAL_COMMA); amount_t x17("$1,000,000.00"); // parsing this switches back to American amount_t x18("$2000"); BOOST_CHECK_EQUAL(string("$2,000.00"), x18.to_string()); x18.parse("$2,000"); BOOST_CHECK_EQUAL(string("$2,000.00"), x18.to_string()); BOOST_CHECK_EQUAL(x15, x17); amount_t x19("EUR 1000"); amount_t x20("EUR 1000"); BOOST_CHECK_EQUAL(string("EUR 1000"), x19.to_string()); BOOST_CHECK_EQUAL(string("EUR 1000"), x20.to_string()); x1.parse("$100.0000", PARSE_NO_MIGRATE); BOOST_CHECK_EQUAL(amount_t::precision_t(2), x12.commodity().precision()); BOOST_CHECK_EQUAL(x1.commodity(), x12.commodity()); BOOST_CHECK_EQUAL(x1, x12); x0.parse("$100.0000"); BOOST_CHECK_EQUAL(amount_t::precision_t(4), x12.commodity().precision()); BOOST_CHECK_EQUAL(x0.commodity(), x12.commodity()); BOOST_CHECK_EQUAL(x0, x12); x2.parse("$100.00", PARSE_NO_REDUCE); BOOST_CHECK_EQUAL(x2, x12); x3.parse("$100.00", PARSE_NO_MIGRATE | PARSE_NO_REDUCE); BOOST_CHECK_EQUAL(x3, x12); x4.parse("$100.00"); BOOST_CHECK_EQUAL(x4, x12); x5.parse("$100.00", PARSE_NO_MIGRATE); BOOST_CHECK_EQUAL(x5, x12); x6.parse("$100.00", PARSE_NO_REDUCE); BOOST_CHECK_EQUAL(x6, x12); x7.parse("$100.00", PARSE_NO_MIGRATE | PARSE_NO_REDUCE); BOOST_CHECK_EQUAL(x7, x12); x8.parse("$100.00"); BOOST_CHECK_EQUAL(x8, x12); x9.parse("$100.00", PARSE_NO_MIGRATE); BOOST_CHECK_EQUAL(x9, x12); x10.parse("$100.00", PARSE_NO_REDUCE); BOOST_CHECK_EQUAL(x10, x12); x11.parse("$100.00", PARSE_NO_MIGRATE | PARSE_NO_REDUCE); BOOST_CHECK_EQUAL(x11, x12); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); BOOST_CHECK(x8.valid()); BOOST_CHECK(x9.valid()); BOOST_CHECK(x10.valid()); BOOST_CHECK(x11.valid()); BOOST_CHECK(x12.valid()); } BOOST_AUTO_TEST_CASE(testConstructors) { amount_t x0; amount_t x1(123456L); amount_t x2(123456UL); amount_t x3("123.456"); amount_t x5("123456"); amount_t x6("123.456"); amount_t x7(string("123456")); amount_t x8(string("123.456")); amount_t x9(x3); amount_t x10(x6); amount_t x11(x8); BOOST_CHECK_EQUAL(amount_t(), x0); BOOST_CHECK_NE(amount_t("0"), x0); BOOST_CHECK_NE(amount_t("0.0"), x0); BOOST_CHECK_EQUAL(x2, x1); BOOST_CHECK_EQUAL(x5, x1); BOOST_CHECK_EQUAL(x7, x1); BOOST_CHECK_EQUAL(x6, x3); BOOST_CHECK_EQUAL(x8, x3); BOOST_CHECK_EQUAL(x10, x3); BOOST_CHECK_EQUAL(x10, x9); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); BOOST_CHECK(x8.valid()); BOOST_CHECK(x9.valid()); BOOST_CHECK(x10.valid()); BOOST_CHECK(x11.valid()); } BOOST_AUTO_TEST_CASE(testCommodityConstructors) { amount_t x1("$123.45"); amount_t x2("-$123.45"); amount_t x3("$-123.45"); amount_t x4("DM 123.45"); amount_t x5("-DM 123.45"); amount_t x6("DM -123.45"); amount_t x7("123.45 euro"); amount_t x8("-123.45 euro"); amount_t x9("123.45€"); amount_t x10("-123.45€"); BOOST_CHECK_EQUAL(amount_t("$123.45"), x1); BOOST_CHECK_EQUAL(amount_t("-$123.45"), x2); BOOST_CHECK_EQUAL(amount_t("$-123.45"), x3); BOOST_CHECK_EQUAL(amount_t("DM 123.45"), x4); BOOST_CHECK_EQUAL(amount_t("-DM 123.45"), x5); BOOST_CHECK_EQUAL(amount_t("DM -123.45"), x6); BOOST_CHECK_EQUAL(amount_t("123.45 euro"), x7); BOOST_CHECK_EQUAL(amount_t("-123.45 euro"), x8); BOOST_CHECK_EQUAL(amount_t("123.45€"), x9); BOOST_CHECK_EQUAL(amount_t("-123.45€"), x10); BOOST_CHECK_EQUAL(string("$123.45"), x1.to_string()); BOOST_CHECK_EQUAL(string("$-123.45"), x2.to_string()); BOOST_CHECK_EQUAL(string("$-123.45"), x3.to_string()); BOOST_CHECK_EQUAL(string("DM 123.45"), x4.to_string()); BOOST_CHECK_EQUAL(string("DM -123.45"), x5.to_string()); BOOST_CHECK_EQUAL(string("DM -123.45"), x6.to_string()); BOOST_CHECK_EQUAL(string("123.45 euro"), x7.to_string()); BOOST_CHECK_EQUAL(string("-123.45 euro"), x8.to_string()); BOOST_CHECK_EQUAL(string("123.45€"), x9.to_string()); BOOST_CHECK_EQUAL(string("-123.45€"), x10.to_string()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); BOOST_CHECK(x8.valid()); BOOST_CHECK(x9.valid()); BOOST_CHECK(x10.valid()); } #ifndef NOT_FOR_PYTHON BOOST_AUTO_TEST_CASE(testAssignment) { amount_t x0; amount_t x1; amount_t x2; amount_t x3; amount_t x5; amount_t x6; amount_t x7; amount_t x8; amount_t x9; amount_t x10; x1 = 123456L; x2 = 123456UL; x3 = "123.456"; x5 = "123456"; x6 = "123.456"; x7 = string("123456"); x8 = string("123.456"); x9 = x3; x10 = amount_t(x6); BOOST_CHECK_EQUAL(x2, x1); BOOST_CHECK_EQUAL(x5, x1); BOOST_CHECK_EQUAL(x7, x1); BOOST_CHECK_EQUAL(x6, x3); BOOST_CHECK_EQUAL(x8, x3); BOOST_CHECK_EQUAL(x10, x3); BOOST_CHECK_EQUAL(x10, x9); BOOST_CHECK(! x1.is_null()); x1 = x0; // sets x1 back to uninitialized state BOOST_CHECK(x0.is_null()); BOOST_CHECK(x1.is_null()); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); BOOST_CHECK(x8.valid()); BOOST_CHECK(x9.valid()); BOOST_CHECK(x10.valid()); } BOOST_AUTO_TEST_CASE(testCommodityAssignment) { amount_t x1; amount_t x2; amount_t x3; amount_t x4; amount_t x5; amount_t x6; amount_t x7; amount_t x8; amount_t x9; amount_t x10; x1 = "$123.45"; x2 = "-$123.45"; x3 = "$-123.45"; x4 = "DM 123.45"; x5 = "-DM 123.45"; x6 = "DM -123.45"; x7 = "123.45 euro"; x8 = "-123.45 euro"; x9 = "123.45€"; x10 = "-123.45€"; BOOST_CHECK_EQUAL(amount_t("$123.45"), x1); BOOST_CHECK_EQUAL(amount_t("-$123.45"), x2); BOOST_CHECK_EQUAL(amount_t("$-123.45"), x3); BOOST_CHECK_EQUAL(amount_t("DM 123.45"), x4); BOOST_CHECK_EQUAL(amount_t("-DM 123.45"), x5); BOOST_CHECK_EQUAL(amount_t("DM -123.45"), x6); BOOST_CHECK_EQUAL(amount_t("123.45 euro"), x7); BOOST_CHECK_EQUAL(amount_t("-123.45 euro"), x8); BOOST_CHECK_EQUAL(amount_t("123.45€"), x9); BOOST_CHECK_EQUAL(amount_t("-123.45€"), x10); BOOST_CHECK_EQUAL(string("$123.45"), x1.to_string()); BOOST_CHECK_EQUAL(string("$-123.45"), x2.to_string()); BOOST_CHECK_EQUAL(string("$-123.45"), x3.to_string()); BOOST_CHECK_EQUAL(string("DM 123.45"), x4.to_string()); BOOST_CHECK_EQUAL(string("DM -123.45"), x5.to_string()); BOOST_CHECK_EQUAL(string("DM -123.45"), x6.to_string()); BOOST_CHECK_EQUAL(string("123.45 euro"), x7.to_string()); BOOST_CHECK_EQUAL(string("-123.45 euro"), x8.to_string()); BOOST_CHECK_EQUAL(string("123.45€"), x9.to_string()); BOOST_CHECK_EQUAL(string("-123.45€"), x10.to_string()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); BOOST_CHECK(x8.valid()); BOOST_CHECK(x9.valid()); BOOST_CHECK(x10.valid()); } #endif // NOT_FOR_PYTHON BOOST_AUTO_TEST_CASE(testEquality) { amount_t x1(123456L); amount_t x2(456789L); amount_t x3(333333L); amount_t x4("123456.0"); amount_t x5("123456.0"); amount_t x6("123456.0"); BOOST_CHECK(x1 == 123456L); BOOST_CHECK(x1 != x2); BOOST_CHECK(x1 == (x2 - x3)); BOOST_CHECK(x1 == x4); BOOST_CHECK(x4 == x5); BOOST_CHECK(x4 == x6); BOOST_CHECK(x1 == 123456L); BOOST_CHECK(123456L == x1); BOOST_CHECK(x1 == 123456UL); BOOST_CHECK(123456UL == x1); BOOST_CHECK(x1 == amount_t("123456.0")); BOOST_CHECK(amount_t("123456.0") == x1); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); } BOOST_AUTO_TEST_CASE(testCommodityEquality) { amount_t x0; amount_t x1("$123.45"); amount_t x2("-$123.45"); amount_t x3("$-123.45"); amount_t x4("DM 123.45"); amount_t x5("-DM 123.45"); amount_t x6("DM -123.45"); amount_t x7("123.45 euro"); amount_t x8("-123.45 euro"); amount_t x9("123.45€"); amount_t x10("-123.45€"); BOOST_CHECK(x0.is_null()); BOOST_CHECK_THROW(x0.is_zero(), amount_error); BOOST_CHECK_THROW(x0.is_realzero(), amount_error); BOOST_CHECK_THROW(x0.sign(), amount_error); BOOST_CHECK_THROW(x0.compare(x1), amount_error); BOOST_CHECK_THROW(x0.compare(x2), amount_error); BOOST_CHECK_THROW(x0.compare(x0), amount_error); BOOST_CHECK(x1 != x2); BOOST_CHECK(x1 != x4); BOOST_CHECK(x1 != x7); BOOST_CHECK(x1 != x9); BOOST_CHECK(x2 == x3); BOOST_CHECK(x4 != x5); BOOST_CHECK(x5 == x6); BOOST_CHECK(x7 == - x8); BOOST_CHECK(x9 == - x10); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); BOOST_CHECK(x8.valid()); BOOST_CHECK(x9.valid()); BOOST_CHECK(x10.valid()); } BOOST_AUTO_TEST_CASE(testComparisons) { amount_t x0; amount_t x1(-123L); amount_t x2(123L); amount_t x3("-123.45"); amount_t x4("123.45"); amount_t x5("-123.45"); amount_t x6("123.45"); BOOST_CHECK_THROW(x0 > x1, amount_error); BOOST_CHECK_THROW(x0 < x2, amount_error); BOOST_CHECK_THROW(x0 > x3, amount_error); BOOST_CHECK_THROW(x0 < x4, amount_error); BOOST_CHECK_THROW(x0 > x5, amount_error); BOOST_CHECK_THROW(x0 < x6, amount_error); BOOST_CHECK(x1 > x3); BOOST_CHECK(x3 <= x5); BOOST_CHECK(x3 >= x5); BOOST_CHECK(x3 < x1); BOOST_CHECK(x3 < x4); BOOST_CHECK(x1 < 100L); BOOST_CHECK(100L > x1); BOOST_CHECK(x1 < 100UL); BOOST_CHECK(100UL > x1); #ifndef NOT_FOR_PYTHON BOOST_CHECK(x1 < 100.0); BOOST_CHECK(100.0 > x1); #endif // NOT_FOR_PYTHON BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); } BOOST_AUTO_TEST_CASE(testCommodityComparisons) { amount_t x1("$-123"); amount_t x2("$123.00"); amount_t x3(internalAmount("$-123.4544")); amount_t x4(internalAmount("$123.4544")); amount_t x5("$-123.45"); amount_t x6("$123.45"); amount_t x7("DM 123.45"); BOOST_CHECK(x1 > x3); BOOST_CHECK(x3 <= x5); BOOST_CHECK(x3 < x5); BOOST_CHECK(x3 <= x5); #ifndef NOT_FOR_PYTHON BOOST_CHECK(! (x3 == x5)); #endif BOOST_CHECK(x3 < x1); BOOST_CHECK(x3 < x4); #ifndef NOT_FOR_PYTHON BOOST_CHECK(! (x6 == x7)); #endif BOOST_CHECK_THROW(x6 < x7, amount_error); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); } BOOST_AUTO_TEST_CASE(testIntegerAddition) { amount_t x0; amount_t x1(123L); amount_t y1(456L); BOOST_CHECK_EQUAL(amount_t(579L), x1 + y1); BOOST_CHECK_EQUAL(amount_t(579L), x1 + 456L); BOOST_CHECK_EQUAL(amount_t(579L), 456L + x1); x1 += amount_t(456L); BOOST_CHECK_EQUAL(amount_t(579L), x1); x1 += 456L; BOOST_CHECK_EQUAL(amount_t(1035L), x1); amount_t x4("123456789123456789123456789"); BOOST_CHECK_EQUAL(amount_t("246913578246913578246913578"), x4 + x4); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(y1.valid()); BOOST_CHECK(x4.valid()); } BOOST_AUTO_TEST_CASE(testFractionalAddition) { amount_t x1("123.123"); amount_t y1("456.456"); BOOST_CHECK_EQUAL(amount_t("579.579"), x1 + y1); BOOST_CHECK_EQUAL(amount_t("579.579"), x1 + amount_t("456.456")); BOOST_CHECK_EQUAL(amount_t("579.579"), amount_t("456.456") + x1); x1 += amount_t("456.456"); BOOST_CHECK_EQUAL(amount_t("579.579"), x1); x1 += amount_t("456.456"); BOOST_CHECK_EQUAL(amount_t("1036.035"), x1); x1 += 456L; BOOST_CHECK_EQUAL(amount_t("1492.035"), x1); amount_t x2("123456789123456789.123456789123456789"); BOOST_CHECK_EQUAL(amount_t("246913578246913578.246913578246913578"), x2 + x2); BOOST_CHECK(x1.valid()); BOOST_CHECK(y1.valid()); BOOST_CHECK(x2.valid()); } BOOST_AUTO_TEST_CASE(testCommodityAddition) { amount_t x0; amount_t x1("$123.45"); amount_t x2(internalAmount("$123.456789")); amount_t x3("DM 123.45"); amount_t x4("123.45 euro"); amount_t x5("123.45€"); amount_t x6("123.45"); BOOST_CHECK_EQUAL(amount_t("$246.90"), x1 + x1); BOOST_CHECK_NE(amount_t("$246.91"), x1 + x2); BOOST_CHECK_EQUAL(internalAmount("$246.906789"), x1 + x2); // Converting to string drops internal precision BOOST_CHECK_EQUAL(string("$246.90"), (x1 + x1).to_string()); BOOST_CHECK_EQUAL(string("$246.91"), (x1 + x2).to_string()); BOOST_CHECK_THROW(x1 + x0, amount_error); BOOST_CHECK_THROW(x0 + x1, amount_error); BOOST_CHECK_THROW(x0 + x0, amount_error); BOOST_CHECK_THROW(x1 + x3, amount_error); BOOST_CHECK_THROW(x1 + x4, amount_error); BOOST_CHECK_THROW(x1 + x5, amount_error); BOOST_CHECK_EQUAL(string("$246.90"), (x1 + x6).to_string()); #ifndef NOT_FOR_PYTHON BOOST_CHECK_EQUAL(string("$246.90"), (x1 + 123.45).to_string()); #endif // NOT_FOR_PYTHON BOOST_CHECK_EQUAL(string("$246.45"), (x1 + 123L).to_string()); BOOST_CHECK_EQUAL(amount_t("DM 246.90"), x3 + x3); BOOST_CHECK_EQUAL(amount_t("246.90 euro"), x4 + x4); BOOST_CHECK_EQUAL(amount_t("246.90€"), x5 + x5); BOOST_CHECK_EQUAL(string("DM 246.90"), (x3 + x3).to_string()); BOOST_CHECK_EQUAL(string("246.90 euro"), (x4 + x4).to_string()); BOOST_CHECK_EQUAL(string("246.90€"), (x5 + x5).to_string()); x1 += amount_t("$456.45"); BOOST_CHECK_EQUAL(amount_t("$579.90"), x1); x1 += amount_t("$456.45"); BOOST_CHECK_EQUAL(amount_t("$1036.35"), x1); x1 += amount_t("$456"); BOOST_CHECK_EQUAL(amount_t("$1492.35"), x1); amount_t x7(internalAmount("$123456789123456789.123456789123456789")); BOOST_CHECK_EQUAL(internalAmount("$246913578246913578.246913578246913578"), x7 + x7); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); } BOOST_AUTO_TEST_CASE(testIntegerSubtraction) { amount_t x1(123L); amount_t y1(456L); BOOST_CHECK_EQUAL(amount_t(333L), y1 - x1); BOOST_CHECK_EQUAL(amount_t(-333L), x1 - y1); BOOST_CHECK_EQUAL(amount_t(23L), x1 - 100L); BOOST_CHECK_EQUAL(amount_t(-23L), 100L - x1); x1 -= amount_t(456L); BOOST_CHECK_EQUAL(amount_t(-333L), x1); x1 -= 456L; BOOST_CHECK_EQUAL(amount_t(-789L), x1); amount_t x4("123456789123456789123456789"); amount_t y4("8238725986235986"); BOOST_CHECK_EQUAL(amount_t("123456789115218063137220803"), x4 - y4); BOOST_CHECK_EQUAL(amount_t("-123456789115218063137220803"), y4 - x4); BOOST_CHECK(x1.valid()); BOOST_CHECK(y1.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(y4.valid()); } BOOST_AUTO_TEST_CASE(testFractionalSubtraction) { amount_t x1("123.123"); amount_t y1("456.456"); BOOST_CHECK_EQUAL(amount_t("-333.333"), x1 - y1); BOOST_CHECK_EQUAL(amount_t("333.333"), y1 - x1); x1 -= amount_t("456.456"); BOOST_CHECK_EQUAL(amount_t("-333.333"), x1); x1 -= amount_t("456.456"); BOOST_CHECK_EQUAL(amount_t("-789.789"), x1); x1 -= 456L; BOOST_CHECK_EQUAL(amount_t("-1245.789"), x1); amount_t x2("123456789123456789.123456789123456789"); amount_t y2("9872345982459.248974239578"); BOOST_CHECK_EQUAL(amount_t("123446916777474329.874482549545456789"), x2 - y2); BOOST_CHECK_EQUAL(amount_t("-123446916777474329.874482549545456789"), y2 - x2); BOOST_CHECK(x1.valid()); BOOST_CHECK(y1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(y2.valid()); } BOOST_AUTO_TEST_CASE(testCommoditySubtraction) { amount_t x0; amount_t x1("$123.45"); amount_t x2(internalAmount("$123.456789")); amount_t x3("DM 123.45"); amount_t x4("123.45 euro"); amount_t x5("123.45€"); amount_t x6("123.45"); BOOST_CHECK_NE(amount_t(), x1 - x1); BOOST_CHECK_EQUAL(amount_t("$0"), x1 - x1); BOOST_CHECK_EQUAL(amount_t("$23.45"), x1 - amount_t("$100.00")); BOOST_CHECK_EQUAL(amount_t("$-23.45"), amount_t("$100.00") - x1); BOOST_CHECK_NE(amount_t("$-0.01"), x1 - x2); BOOST_CHECK_EQUAL(internalAmount("$-0.006789"), x1 - x2); // Converting to string drops internal precision. If an amount is // zero, it drops the commodity as well. BOOST_CHECK_EQUAL(string("$0.00"), (x1 - x1).to_string()); BOOST_CHECK_EQUAL(string("$-0.01"), (x1 - x2).to_string()); BOOST_CHECK_THROW(x1 - x0, amount_error); BOOST_CHECK_THROW(x0 - x1, amount_error); BOOST_CHECK_THROW(x0 - x0, amount_error); BOOST_CHECK_THROW(x1 - x3, amount_error); BOOST_CHECK_THROW(x1 - x4, amount_error); BOOST_CHECK_THROW(x1 - x5, amount_error); BOOST_CHECK_EQUAL(string("$0.00"), (x1 - x6).to_string()); #ifndef NOT_FOR_PYTHON BOOST_CHECK_EQUAL(string("$-0.00"), (x1 - 123.45).to_string()); #endif // NOT_FOR_PYTHON BOOST_CHECK_EQUAL(string("$0.45"), (x1 - 123L).to_string()); BOOST_CHECK_EQUAL(amount_t("DM 0.00"), x3 - x3); BOOST_CHECK_EQUAL(amount_t("DM 23.45"), x3 - amount_t("DM 100.00")); BOOST_CHECK_EQUAL(amount_t("DM -23.45"), amount_t("DM 100.00") - x3); BOOST_CHECK_EQUAL(amount_t("0.00 euro"), x4 - x4); BOOST_CHECK_EQUAL(amount_t("23.45 euro"), x4 - amount_t("100.00 euro")); BOOST_CHECK_EQUAL(amount_t("-23.45 euro"), amount_t("100.00 euro") - x4); BOOST_CHECK_EQUAL(amount_t("0.00€"), x5 - x5); BOOST_CHECK_EQUAL(amount_t("23.45€"), x5 - amount_t("100.00€")); BOOST_CHECK_EQUAL(amount_t("-23.45€"), amount_t("100.00€") - x5); BOOST_CHECK_EQUAL(string("DM 0.00"), (x3 - x3).to_string()); BOOST_CHECK_EQUAL(string("DM 23.45"), (x3 - amount_t("DM 100.00")).to_string()); BOOST_CHECK_EQUAL(string("DM -23.45"), (amount_t("DM 100.00") - x3).to_string()); BOOST_CHECK_EQUAL(string("0.00 euro"), (x4 - x4).to_string()); BOOST_CHECK_EQUAL(string("23.45 euro"), (x4 - amount_t("100.00 euro")).to_string()); BOOST_CHECK_EQUAL(string("-23.45 euro"), (amount_t("100.00 euro") - x4).to_string()); BOOST_CHECK_EQUAL(string("0.00€"), (x5 - x5).to_string()); BOOST_CHECK_EQUAL(string("23.45€"), (x5 - amount_t("100.00€")).to_string()); BOOST_CHECK_EQUAL(string("-23.45€"), (amount_t("100.00€") - x5).to_string()); x1 -= amount_t("$456.45"); BOOST_CHECK_EQUAL(amount_t("$-333.00"), x1); x1 -= amount_t("$456.45"); BOOST_CHECK_EQUAL(amount_t("$-789.45"), x1); x1 -= amount_t("$456"); BOOST_CHECK_EQUAL(amount_t("$-1245.45"), x1); amount_t x7(internalAmount("$123456789123456789.123456789123456789")); amount_t x8(internalAmount("$2354974984698.98459845984598")); BOOST_CHECK_EQUAL(internalAmount("$123454434148472090.138858329277476789"), x7 - x8); BOOST_CHECK_EQUAL(string("$123454434148472090.138858329277476789"), (x7 - x8).to_string()); BOOST_CHECK_EQUAL(string("$123454434148472090.14"), (amount_t("$1.00") * (x7 - x8)).to_string()); BOOST_CHECK_EQUAL(internalAmount("$-123454434148472090.138858329277476789"), x8 - x7); BOOST_CHECK_EQUAL(string("$-123454434148472090.138858329277476789"), (x8 - x7).to_string()); BOOST_CHECK_EQUAL(string("$-123454434148472090.14"), (amount_t("$1.00") * (x8 - x7)).to_string()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); BOOST_CHECK(x8.valid()); } BOOST_AUTO_TEST_CASE(testIntegerMultiplication) { amount_t x1(123L); amount_t y1(456L); BOOST_CHECK_EQUAL(amount_t(0L), x1 * 0L); BOOST_CHECK_EQUAL(amount_t(0L), amount_t(0L) * x1); BOOST_CHECK_EQUAL(amount_t(0L), 0L * x1); BOOST_CHECK_EQUAL(x1, x1 * 1L); BOOST_CHECK_EQUAL(x1, amount_t(1L) * x1); BOOST_CHECK_EQUAL(x1, 1L * x1); BOOST_CHECK_EQUAL(- x1, x1 * -1L); BOOST_CHECK_EQUAL(- x1, amount_t(-1L) * x1); BOOST_CHECK_EQUAL(- x1, -1L * x1); BOOST_CHECK_EQUAL(amount_t(56088L), x1 * y1); BOOST_CHECK_EQUAL(amount_t(56088L), y1 * x1); BOOST_CHECK_EQUAL(amount_t(56088L), x1 * 456L); BOOST_CHECK_EQUAL(amount_t(56088L), amount_t(456L) * x1); BOOST_CHECK_EQUAL(amount_t(56088L), 456L * x1); x1 *= amount_t(123L); BOOST_CHECK_EQUAL(amount_t(15129L), x1); x1 *= 123L; BOOST_CHECK_EQUAL(amount_t(1860867L), x1); amount_t x4("123456789123456789123456789"); BOOST_CHECK_EQUAL(amount_t("15241578780673678546105778281054720515622620750190521"), x4 * x4); BOOST_CHECK(x1.valid()); BOOST_CHECK(y1.valid()); BOOST_CHECK(x4.valid()); } BOOST_AUTO_TEST_CASE(testFractionalMultiplication) { amount_t x1("123.123"); amount_t y1("456.456"); BOOST_CHECK_EQUAL(amount_t(0L), x1 * 0L); BOOST_CHECK_EQUAL(amount_t(0L), amount_t(0L) * x1); BOOST_CHECK_EQUAL(amount_t(0L), 0L * x1); BOOST_CHECK_EQUAL(x1, x1 * 1L); BOOST_CHECK_EQUAL(x1, amount_t(1L) * x1); BOOST_CHECK_EQUAL(x1, 1L * x1); BOOST_CHECK_EQUAL(- x1, x1 * -1L); BOOST_CHECK_EQUAL(- x1, amount_t(-1L) * x1); BOOST_CHECK_EQUAL(- x1, -1L * x1); BOOST_CHECK_EQUAL(amount_t("56200.232088"), x1 * y1); BOOST_CHECK_EQUAL(amount_t("56200.232088"), y1 * x1); BOOST_CHECK_EQUAL(amount_t("56200.232088"), x1 * amount_t("456.456")); BOOST_CHECK_EQUAL(amount_t("56200.232088"), amount_t("456.456") * x1); BOOST_CHECK_EQUAL(amount_t("56200.232088"), amount_t("456.456") * x1); x1 *= amount_t("123.123"); BOOST_CHECK_EQUAL(amount_t("15159.273129"), x1); x1 *= amount_t("123.123"); BOOST_CHECK_EQUAL(amount_t("1866455.185461867"), x1); x1 *= 123L; BOOST_CHECK_EQUAL(amount_t("229573987.811809641"), x1); amount_t x2("123456789123456789.123456789123456789"); BOOST_CHECK_EQUAL(amount_t("15241578780673678546105778311537878.046486820281054720515622620750190521"), x2 * x2); BOOST_CHECK(x1.valid()); BOOST_CHECK(y1.valid()); BOOST_CHECK(x2.valid()); } BOOST_AUTO_TEST_CASE(testCommodityMultiplication) { amount_t x0; amount_t x1("$123.12"); amount_t y1("$456.45"); amount_t x2(internalAmount("$123.456789")); amount_t x3("DM 123.45"); amount_t x4("123.45 euro"); amount_t x5("123.45€"); BOOST_CHECK_EQUAL(amount_t("$0.00"), x1 * 0L); BOOST_CHECK_EQUAL(amount_t("$0.00"), 0L * x1); BOOST_CHECK_EQUAL(x1, x1 * 1L); BOOST_CHECK_EQUAL(x1, 1L * x1); BOOST_CHECK_EQUAL(- x1, x1 * -1L); BOOST_CHECK_EQUAL(- x1, -1L * x1); BOOST_CHECK_EQUAL(internalAmount("$56198.124"), x1 * y1); BOOST_CHECK_EQUAL(string("$56198.12"), (x1 * y1).to_string()); BOOST_CHECK_EQUAL(internalAmount("$56198.124"), y1 * x1); BOOST_CHECK_EQUAL(string("$56198.12"), (y1 * x1).to_string()); // Internal amounts retain their precision, even when being // converted to strings BOOST_CHECK_EQUAL(internalAmount("$15199.99986168"), x1 * x2); BOOST_CHECK_EQUAL(internalAmount("$15199.99986168"), x2 * x1); BOOST_CHECK_EQUAL(string("$15200.00"), (x1 * x2).to_string()); BOOST_CHECK_EQUAL(string("$15199.99986168"), (x2 * x1).to_string()); BOOST_CHECK_THROW(x1 * x0, amount_error); BOOST_CHECK_THROW(x0 * x1, amount_error); BOOST_CHECK_THROW(x0 * x0, amount_error); //BOOST_CHECK_THROW(x1 * x3, amount_error); //BOOST_CHECK_THROW(x1 * x4, amount_error); //BOOST_CHECK_THROW(x1 * x5, amount_error); x1 *= amount_t("123.12"); BOOST_CHECK_EQUAL(internalAmount("$15158.5344"), x1); BOOST_CHECK_EQUAL(string("$15158.53"), x1.to_string()); x1 *= amount_t("123.12"); BOOST_CHECK_EQUAL(internalAmount("$1866318.755328"), x1); BOOST_CHECK_EQUAL(string("$1866318.76"), x1.to_string()); x1 *= 123L; BOOST_CHECK_EQUAL(internalAmount("$229557206.905344"), x1); BOOST_CHECK_EQUAL(string("$229557206.91"), x1.to_string()); amount_t x7(internalAmount("$123456789123456789.123456789123456789")); BOOST_CHECK_EQUAL(internalAmount("$15241578780673678546105778311537878.046486820281054720515622620750190521"), x7 * x7); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x7.valid()); } BOOST_AUTO_TEST_CASE(testIntegerDivision) { amount_t x1(123L); amount_t y1(456L); BOOST_CHECK_THROW(x1 / 0L, amount_error); BOOST_CHECK_EQUAL(amount_t(0L), amount_t(0L) / x1); BOOST_CHECK_EQUAL(amount_t(0L), 0L / x1); BOOST_CHECK_EQUAL(x1, x1 / 1L); BOOST_CHECK_EQUAL(string("0.00813"), (amount_t(1L) / x1).to_string()); BOOST_CHECK_EQUAL(string("0.00813"), (1L / x1).to_string()); BOOST_CHECK_EQUAL(- x1, x1 / -1L); BOOST_CHECK_EQUAL(string("-0.00813"), (amount_t(-1L) / x1).to_string()); BOOST_CHECK_EQUAL(string("-0.00813"), (-1L / x1).to_string()); BOOST_CHECK_EQUAL(string("0.269737"), (x1 / y1).to_string()); BOOST_CHECK_EQUAL(string("3.707317"), (y1 / x1).to_string()); BOOST_CHECK_EQUAL(string("0.269737"), (x1 / 456L).to_string()); BOOST_CHECK_EQUAL(string("3.707317"), (amount_t(456L) / x1).to_string()); BOOST_CHECK_EQUAL(string("3.707317"), (456L / x1).to_string()); x1 /= amount_t(456L); BOOST_CHECK_EQUAL(string("0.269737"), x1.to_string()); x1 /= 456L; BOOST_CHECK_EQUAL(string("0.000591528163"), x1.to_string()); amount_t x4("123456789123456789123456789"); amount_t y4("56"); BOOST_CHECK_EQUAL(amount_t(1L), x4 / x4); BOOST_CHECK_EQUAL(string("2204585520061728377204585.517857"), (x4 / y4).to_string()); BOOST_CHECK_EQUAL(amount_t("0.000000000000000000000000000001"), amount_t("10") / amount_t("10000000000000000000000000000000")); BOOST_CHECK(x1.valid()); BOOST_CHECK(y1.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(y4.valid()); } BOOST_AUTO_TEST_CASE(testFractionalDivision) { amount_t x1("123.123"); amount_t y1("456.456"); BOOST_CHECK_THROW(x1 / 0L, amount_error); BOOST_CHECK_EQUAL(string("0.0081219593"), (amount_t("1.0") / x1).to_string()); BOOST_CHECK_EQUAL(string("0.0081219593"), (amount_t("1.0") / x1).to_string()); BOOST_CHECK_EQUAL(x1, x1 / amount_t("1.0")); BOOST_CHECK_EQUAL(string("0.0081219593"), (amount_t("1.0") / x1).to_string()); BOOST_CHECK_EQUAL(string("0.0081219593"), (amount_t("1.0") / x1).to_string()); BOOST_CHECK_EQUAL(- x1, x1 / amount_t("-1.0")); BOOST_CHECK_EQUAL(string("-0.0081219593"), (amount_t("-1.0") / x1).to_string()); BOOST_CHECK_EQUAL(string("-0.0081219593"), (amount_t("-1.0") / x1).to_string()); BOOST_CHECK_EQUAL(string("0.269736842105"), (x1 / y1).to_string()); BOOST_CHECK_EQUAL(string("3.707317073171"), (y1 / x1).to_string()); BOOST_CHECK_EQUAL(string("0.269736842105"), (x1 / amount_t("456.456")).to_string()); BOOST_CHECK_EQUAL(string("3.707317073171"), (amount_t("456.456") / x1).to_string()); BOOST_CHECK_EQUAL(string("3.707317073171"), (amount_t("456.456") / x1).to_string()); x1 /= amount_t("456.456"); BOOST_CHECK_EQUAL(string("0.269736842105"), x1.to_string()); x1 /= amount_t("456.456"); BOOST_CHECK_EQUAL(string("0.000590937225286255757"), x1.to_string()); x1 /= 456L; BOOST_CHECK_EQUAL(string("0.000001295914967733017011337"), x1.to_string()); amount_t x4("1234567891234567.89123456789"); amount_t y4("56.789"); BOOST_CHECK_EQUAL(amount_t("1.0"), x4 / x4); BOOST_CHECK_EQUAL(string("21739560323910.75544972737484371973"), (x4 / y4).to_string()); BOOST_CHECK(x1.valid()); BOOST_CHECK(y1.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(y4.valid()); } BOOST_AUTO_TEST_CASE(testCommodityDivision) { amount_t x0; amount_t x1("$123.12"); amount_t y1("$456.45"); amount_t x2(internalAmount("$123.456789")); amount_t x3("DM 123.45"); amount_t x4("123.45 euro"); amount_t x5("123.45€"); BOOST_CHECK_THROW(x1 / 0L, amount_error); BOOST_CHECK_EQUAL(amount_t("$0.00"), 0L / x1); BOOST_CHECK_EQUAL(x1, x1 / 1L); BOOST_CHECK_EQUAL(string("$0.00812216"), (1L / x1).to_fullstring()); BOOST_CHECK_EQUAL(- x1, x1 / -1L); BOOST_CHECK_EQUAL(string("$-0.00812216"), (-1L / x1).to_fullstring()); BOOST_CHECK_EQUAL(string("$0.26973382"), (x1 / y1).to_fullstring()); BOOST_CHECK_EQUAL(string("$0.27"), (x1 / y1).to_string()); BOOST_CHECK_EQUAL(string("$3.70735867"), (y1 / x1).to_fullstring()); BOOST_CHECK_EQUAL(string("$3.71"), (y1 / x1).to_string()); // Internal amounts retain their precision, even when being // converted to strings BOOST_CHECK_EQUAL(string("$0.99727201"), (x1 / x2).to_fullstring()); BOOST_CHECK_EQUAL(string("$1.00273545321637"), (x2 / x1).to_fullstring()); BOOST_CHECK_EQUAL(string("$1.00"), (x1 / x2).to_string()); BOOST_CHECK_EQUAL(string("$1.00273545321637"), (x2 / x1).to_string()); BOOST_CHECK_THROW(x1 / x0, amount_error); BOOST_CHECK_THROW(x0 / x1, amount_error); BOOST_CHECK_THROW(x0 / x0, amount_error); //BOOST_CHECK_THROW(x1 / x3, amount_error); //BOOST_CHECK_THROW(x1 / x4, amount_error); //BOOST_CHECK_THROW(x1 / x5, amount_error); x1 /= amount_t("123.12"); BOOST_CHECK_EQUAL(string("$1.00"), x1.to_string()); x1 /= amount_t("123.12"); BOOST_CHECK_EQUAL(string("$0.00812216"), x1.to_fullstring()); BOOST_CHECK_EQUAL(string("$0.01"), x1.to_string()); x1 /= 123L; BOOST_CHECK_EQUAL(string("$0.00006603"), x1.to_fullstring()); BOOST_CHECK_EQUAL(string("$0.00"), x1.to_string()); amount_t x6(internalAmount("$237235987235987.98723987235978")); amount_t x7(internalAmount("$123456789123456789.123456789123456789")); BOOST_CHECK_EQUAL(amount_t("$1"), x7 / x7); BOOST_CHECK_EQUAL(string("$0.0019216115121765559608381226612019501"), (x6 / x7).to_fullstring()); BOOST_CHECK_EQUAL(string("$520.39654928343335571379527154924040947272"), (x7 / x6).to_fullstring()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); } BOOST_AUTO_TEST_CASE(testNegation) { amount_t x0; amount_t x1(-123456L); amount_t x3("-123.456"); amount_t x5("-123456"); amount_t x6("-123.456"); amount_t x7(string("-123456")); amount_t x8(string("-123.456")); amount_t x9(- x3); BOOST_CHECK_THROW(x0.negated(), amount_error); BOOST_CHECK_EQUAL(x5, x1); BOOST_CHECK_EQUAL(x7, x1); BOOST_CHECK_EQUAL(x6, x3); BOOST_CHECK_EQUAL(x8, x3); BOOST_CHECK_EQUAL(- x6, x9); BOOST_CHECK_EQUAL(x3.negated(), x9); amount_t x10(x9.negated()); BOOST_CHECK_EQUAL(x3, x10); BOOST_CHECK(x1.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); BOOST_CHECK(x8.valid()); BOOST_CHECK(x9.valid()); BOOST_CHECK(x10.valid()); } BOOST_AUTO_TEST_CASE(testCommodityNegation) { amount_t x1("$123.45"); amount_t x2("-$123.45"); amount_t x3("$-123.45"); amount_t x4("DM 123.45"); amount_t x5("-DM 123.45"); amount_t x6("DM -123.45"); amount_t x7("123.45 euro"); amount_t x8("-123.45 euro"); amount_t x9("123.45€"); amount_t x10("-123.45€"); BOOST_CHECK_EQUAL(amount_t("$-123.45"), - x1); BOOST_CHECK_EQUAL(amount_t("$123.45"), - x2); BOOST_CHECK_EQUAL(amount_t("$123.45"), - x3); BOOST_CHECK_EQUAL(amount_t("DM -123.45"), - x4); BOOST_CHECK_EQUAL(amount_t("DM 123.45"), - x5); BOOST_CHECK_EQUAL(amount_t("DM 123.45"), - x6); BOOST_CHECK_EQUAL(amount_t("-123.45 euro"), - x7); BOOST_CHECK_EQUAL(amount_t("123.45 euro"), - x8); BOOST_CHECK_EQUAL(amount_t("-123.45€"), - x9); BOOST_CHECK_EQUAL(amount_t("123.45€"), - x10); BOOST_CHECK_EQUAL(amount_t("$-123.45"), x1.negated()); BOOST_CHECK_EQUAL(amount_t("$123.45"), x2.negated()); BOOST_CHECK_EQUAL(amount_t("$123.45"), x3.negated()); BOOST_CHECK_EQUAL(string("$-123.45"), (- x1).to_string()); BOOST_CHECK_EQUAL(string("$123.45"), (- x2).to_string()); BOOST_CHECK_EQUAL(string("$123.45"), (- x3).to_string()); BOOST_CHECK_EQUAL(string("DM -123.45"), (- x4).to_string()); BOOST_CHECK_EQUAL(string("DM 123.45"), (- x5).to_string()); BOOST_CHECK_EQUAL(string("DM 123.45"), (- x6).to_string()); BOOST_CHECK_EQUAL(string("-123.45 euro"), (- x7).to_string()); BOOST_CHECK_EQUAL(string("123.45 euro"), (- x8).to_string()); BOOST_CHECK_EQUAL(string("-123.45€"), (- x9).to_string()); BOOST_CHECK_EQUAL(string("123.45€"), (- x10).to_string()); BOOST_CHECK_EQUAL(amount_t("$-123.45"), x1.negated()); BOOST_CHECK_EQUAL(amount_t("$123.45"), x2.negated()); BOOST_CHECK_EQUAL(amount_t("$123.45"), x3.negated()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); BOOST_CHECK(x5.valid()); BOOST_CHECK(x6.valid()); BOOST_CHECK(x7.valid()); BOOST_CHECK(x8.valid()); BOOST_CHECK(x9.valid()); BOOST_CHECK(x10.valid()); } BOOST_AUTO_TEST_CASE(testAbs) { amount_t x0; amount_t x1(-1234L); amount_t x2(1234L); BOOST_CHECK_THROW(x0.abs(), amount_error); BOOST_CHECK_EQUAL(amount_t(1234L), x1.abs()); BOOST_CHECK_EQUAL(amount_t(1234L), x2.abs()); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); } BOOST_AUTO_TEST_CASE(testCommodityAbs) { amount_t x1("$-1234.56"); amount_t x2("$1234.56"); BOOST_CHECK_EQUAL(amount_t("$1234.56"), x1.abs()); BOOST_CHECK_EQUAL(amount_t("$1234.56"), x2.abs()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); } BOOST_AUTO_TEST_CASE(testFloor) { amount_t x0; amount_t x1("123.123"); amount_t x2("-123.123"); BOOST_CHECK_THROW(x0.floored(), amount_error); BOOST_CHECK_EQUAL(amount_t(123L), x1.floored()); BOOST_CHECK_EQUAL(amount_t(-124L), x2.floored()); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); } BOOST_AUTO_TEST_CASE(testCommodityFloor) { amount_t x1("$1234.56"); amount_t x2("$-1234.56"); BOOST_CHECK_EQUAL(amount_t("$1234"), x1.floored()); BOOST_CHECK_EQUAL(amount_t("$-1235"), x2.floored()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); } BOOST_AUTO_TEST_CASE(testCeiling) { amount_t x0; amount_t x1("123.123"); amount_t x2("-123.123"); BOOST_CHECK_THROW(x0.ceilinged(), amount_error); BOOST_CHECK_EQUAL(amount_t(124L), x1.ceilinged()); BOOST_CHECK_EQUAL(amount_t(-123L), x2.ceilinged()); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); } BOOST_AUTO_TEST_CASE(testCommodityCeiling) { amount_t x1("$1234.56"); amount_t x2("$-1234.56"); BOOST_CHECK_EQUAL(amount_t("$1235"), x1.ceilinged()); BOOST_CHECK_EQUAL(amount_t("$-1234"), x2.ceilinged()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); } #ifndef NOT_FOR_PYTHON #if 0 BOOST_AUTO_TEST_CASE(testReduction) { amount_t x0; amount_t x1("60s"); amount_t x2("600s"); amount_t x3("6000s"); amount_t x4("360000s"); amount_t x5("10m"); // 600s amount_t x6("100m"); // 6000s amount_t x7("1000m"); // 60000s amount_t x8("10000m"); // 600000s amount_t x9("10h"); // 36000s amount_t x10("100h"); // 360000s amount_t x11("1000h"); // 3600000s amount_t x12("10000h"); // 36000000s BOOST_CHECK_THROW(x0.reduce(), amount_error); BOOST_CHECK_THROW(x0.unreduce(), amount_error); BOOST_CHECK_EQUAL(x2, x5.reduce()); BOOST_CHECK_EQUAL(x3, x6.reduce()); BOOST_CHECK_EQUAL(x10, x4.reduce()); BOOST_CHECK_EQUAL(string("100.0h"), x4.unreduce().to_string()); } #endif #endif // NOT_FOR_PYTHON BOOST_AUTO_TEST_CASE(testSign) { amount_t x0; amount_t x1("0.0000000000000000000000000000000000001"); amount_t x2("-0.0000000000000000000000000000000000001"); amount_t x3("1"); amount_t x4("-1"); BOOST_CHECK_THROW(x0.sign(), amount_error); BOOST_CHECK(x1.sign() > 0); BOOST_CHECK(x2.sign() < 0); BOOST_CHECK(x3.sign() > 0); BOOST_CHECK(x4.sign() < 0); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); } BOOST_AUTO_TEST_CASE(testCommoditySign) { amount_t x1(internalAmount("$0.0000000000000000000000000000000000001")); amount_t x2(internalAmount("$-0.0000000000000000000000000000000000001")); amount_t x3("$1"); amount_t x4("$-1"); BOOST_CHECK(x1.sign() != 0); BOOST_CHECK(x2.sign() != 0); BOOST_CHECK(x3.sign() > 0); BOOST_CHECK(x4.sign() < 0); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); BOOST_CHECK(x3.valid()); BOOST_CHECK(x4.valid()); } BOOST_AUTO_TEST_CASE(testTruth) { amount_t x0; amount_t x1("1234"); amount_t x2("1234.56"); #ifndef NOT_FOR_PYTHON BOOST_CHECK_THROW(x0.operator bool(), amount_error); #endif // NOT_FOR_PYTHON BOOST_CHECK(x1); BOOST_CHECK(x2); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); } BOOST_AUTO_TEST_CASE(testCommodityTruth) { amount_t x1("$1234"); amount_t x2("$1234.56"); if (x1) BOOST_CHECK(true); if (x2) BOOST_CHECK(true); BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); } BOOST_AUTO_TEST_CASE(testForZero) { amount_t x0; amount_t x1("0.000000000000000000001"); BOOST_CHECK(x1); BOOST_CHECK_THROW(x0.is_zero(), amount_error); BOOST_CHECK_THROW(x0.is_realzero(), amount_error); BOOST_CHECK(! x1.is_zero()); BOOST_CHECK(! x1.is_realzero()); BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); } BOOST_AUTO_TEST_CASE(testCommodityForZero) { amount_t x1(internalAmount("$0.000000000000000000001")); BOOST_CHECK(x1); // an internal amount never betrays its precision BOOST_CHECK(! x1.is_zero()); BOOST_CHECK(! x1.is_realzero()); BOOST_CHECK(x1.valid()); } BOOST_AUTO_TEST_CASE(testIntegerConversion) { amount_t x0; amount_t x1(123456L); amount_t x2("12345682348723487324"); BOOST_CHECK_THROW(x0.to_long(), amount_error); BOOST_CHECK_THROW(x0.to_double(), amount_error); BOOST_CHECK(! x2.fits_in_long()); BOOST_CHECK_EQUAL(123456L, x1.to_long()); BOOST_CHECK_EQUAL(123456.0, x1.to_double()); BOOST_CHECK_EQUAL(string("123456"), x1.to_string()); BOOST_CHECK_EQUAL(string("123456"), x1.quantity_string()); BOOST_CHECK(x1.valid()); } BOOST_AUTO_TEST_CASE(testFractionalConversion) { amount_t x1("1234.56"); amount_t x2("1234.5683787634678348734"); BOOST_CHECK_EQUAL(1235L, x1.to_long()); BOOST_CHECK_EQUAL(1234.56, x1.to_double()); BOOST_CHECK_EQUAL(string("1234.56"), x1.to_string()); BOOST_CHECK_EQUAL(string("1234.56"), x1.quantity_string()); BOOST_CHECK(x1.valid()); } BOOST_AUTO_TEST_CASE(testCommodityConversion) { amount_t x1("$1234.56"); BOOST_CHECK_EQUAL(1235L, x1.to_long()); BOOST_CHECK_EQUAL(1234.56, x1.to_double()); BOOST_CHECK_EQUAL(string("$1234.56"), x1.to_string()); BOOST_CHECK_EQUAL(string("1234.56"), x1.quantity_string()); BOOST_CHECK(x1.valid()); } #ifndef NOT_FOR_PYTHON BOOST_AUTO_TEST_CASE(testPrinting) { amount_t x0; amount_t x1("982340823.380238098235098235098235098"); { std::ostringstream bufstr; x0.print(bufstr); BOOST_CHECK_EQUAL(std::string("<null>"), bufstr.str()); } { std::ostringstream bufstr; x1.print(bufstr); BOOST_CHECK_EQUAL(std::string("982340823.380238098235098235098235098"), bufstr.str()); } BOOST_CHECK(x0.valid()); BOOST_CHECK(x1.valid()); } BOOST_AUTO_TEST_CASE(testCommodityPrinting) { amount_t x1(internalAmount("$982340823.386238098235098235098235098")); amount_t x2("$982340823.38"); { std::ostringstream bufstr; x1.print(bufstr); BOOST_CHECK_EQUAL(std::string("$982340823.386238098235098235098235098"), bufstr.str()); } { std::ostringstream bufstr; (x1 * x2).print(bufstr); BOOST_CHECK_EQUAL(std::string("$964993493285024293.18099172508158508135413499124"), bufstr.str()); } { std::ostringstream bufstr; (x2 * x1).print(bufstr); BOOST_CHECK_EQUAL(std::string("$964993493285024293.18"), bufstr.str()); } BOOST_CHECK(x1.valid()); BOOST_CHECK(x2.valid()); } #endif // NOT_FOR_PYTHON BOOST_AUTO_TEST_SUITE_END() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/unit/t_balance.cc�����������������������������������������������������������������0000664�0000000�0000000�00000024124�14411236400�0017134�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK //#define BOOST_TEST_MODULE balance #include <boost/test/unit_test.hpp> #include <system.hh> #include "balance.h" using namespace ledger; struct balance_fixture { balance_fixture() { times_initialize(); amount_t::initialize(); // Cause the display precision for dollars to be initialized to 2. amount_t x1("$1.00"); BOOST_CHECK(x1); amount_t::stream_fullstrings = true; // make reports from UnitTests accurate } ~balance_fixture() { amount_t::stream_fullstrings = false; amount_t::shutdown(); times_shutdown(); } }; BOOST_FIXTURE_TEST_SUITE(balance, balance_fixture) BOOST_AUTO_TEST_CASE(testConstructors) { balance_t b0; balance_t b1(1.00); balance_t b2(123456UL); balance_t b3(12345L); balance_t b4(string ("EUR 123")); balance_t b5("$ 456"); balance_t b6; balance_t b7(amount_t("$ 1.00")); balance_t b8(b7); BOOST_CHECK_EQUAL(balance_t(), b0); BOOST_CHECK_NE(balance_t("0"), b0); BOOST_CHECK_NE(balance_t("0.0"), b0); BOOST_CHECK_EQUAL(b2, 123456UL); BOOST_CHECK_EQUAL(b3, 12345L); BOOST_CHECK_EQUAL(b4, "EUR 123"); BOOST_CHECK_EQUAL(b5, string("$ 456")); BOOST_CHECK_EQUAL(b7, b8); BOOST_CHECK_EQUAL(b8, amount_t("$ 1.00")); b5 = "euro 2345"; b6 = string("DM -34532"); b7 = amount_t("$ 1.00"); b8 = b5; BOOST_CHECK_EQUAL(b5, b8); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); BOOST_CHECK(b4.valid()); BOOST_CHECK(b5.valid()); BOOST_CHECK(b6.valid()); BOOST_CHECK(b7.valid()); BOOST_CHECK(b8.valid()); } BOOST_AUTO_TEST_CASE(testAddition) { amount_t a0; amount_t a1("$1"); amount_t a2("2 EUR"); amount_t a3("0.00 CAD"); amount_t a4("$2"); balance_t b0; balance_t b1(1.00); balance_t b2(2UL); balance_t b3(2L); balance_t b4; balance_t b5; b0 += b1; b2 += b3; b3 += a1; b3 += a2; b4 += b3; b5 += a1; b5 += a4; BOOST_CHECK_EQUAL(balance_t(1.00), b0); BOOST_CHECK_EQUAL(b3 += a3, b4); BOOST_CHECK_EQUAL(balance_t(4L), b2); BOOST_CHECK_EQUAL(balance_t() += amount_t("$3"), b5); BOOST_CHECK_THROW(b3 += a0, balance_error); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); BOOST_CHECK(b4.valid()); BOOST_CHECK(b5.valid()); } BOOST_AUTO_TEST_CASE(testSubtraction) { amount_t a0; amount_t a1("$1"); amount_t a2("2 EUR"); amount_t a3("0.00 CAD"); amount_t a4("$2"); balance_t b0; balance_t b1(1.00); balance_t b2(2UL); balance_t b3(2L); balance_t b4; balance_t b5; b0 -= b1; b2 -= b3; b3 -= a1; b3 -= a2; b4 = b3; b5 -= a1; b5 -= a4; BOOST_CHECK_EQUAL(balance_t(-1.00), b0); BOOST_CHECK_EQUAL(b3 -= a3, b4); BOOST_CHECK_EQUAL(balance_t(), b2); BOOST_CHECK_EQUAL(b3 -= b2, b3); BOOST_CHECK_EQUAL(balance_t() -= amount_t("$3"), b5); BOOST_CHECK_THROW(b3 -= a0, balance_error); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); BOOST_CHECK(b4.valid()); BOOST_CHECK(b5.valid()); } BOOST_AUTO_TEST_CASE(testEqaulity) { amount_t a0; amount_t a1("$1"); amount_t a2("2 EUR"); amount_t a3("0.00 CAD"); balance_t b0; balance_t b1(1.00); balance_t b2(2UL); balance_t b3(2L); balance_t b4("EUR 2"); balance_t b5("$-1"); balance_t b6("0.00"); balance_t b7("0.00"); BOOST_CHECK(b2 == b3); BOOST_CHECK(b4 == a2); BOOST_CHECK(b1 == "1.00"); BOOST_CHECK(b5 == amount_t("-$1")); BOOST_CHECK(!(b6 == "0")); BOOST_CHECK(!(b6 == a3)); BOOST_CHECK(!(b6 == "0.00")); BOOST_CHECK(b6 == b7); b4 += b5; b5 += a2; BOOST_CHECK(b4 == b5); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-value" BOOST_CHECK_THROW(b0 == a0, balance_error); #pragma GCC diagnostic pop BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); BOOST_CHECK(b4.valid()); BOOST_CHECK(b5.valid()); BOOST_CHECK(b6.valid()); BOOST_CHECK(b7.valid()); } BOOST_AUTO_TEST_CASE(testMultiplication) { amount_t a0; amount_t a1("0.00"); balance_t b0; balance_t b1(1.00); balance_t b2(2UL); balance_t b3("CAD -3"); balance_t b4("EUR 4.99999"); balance_t b5("$1"); balance_t b6; BOOST_CHECK_EQUAL(b1 *= 2.00, amount_t(2.00)); BOOST_CHECK_EQUAL(b2 *= 2L, amount_t(4L)); BOOST_CHECK_EQUAL(b2 *= 2UL, amount_t(8UL)); BOOST_CHECK_EQUAL(b3 *= amount_t("-8 CAD"), amount_t("CAD 24")); BOOST_CHECK_EQUAL(b0 *= 2UL, b0); BOOST_CHECK_EQUAL(b0 *= a1, a1); b6 += b3; b3 += b4; b3 += b5; b3 *= 2L; b6 *= 2L; b4 *= 2L; b5 *= 2L; b6 += b4; b6 += b5; BOOST_CHECK_EQUAL(b3, b6); BOOST_CHECK_THROW(b1 *= a0 , balance_error); BOOST_CHECK_THROW(b4 *= amount_t("1 CAD") , balance_error); BOOST_CHECK_THROW(b3 *= amount_t("1 CAD") , balance_error); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); BOOST_CHECK(b4.valid()); BOOST_CHECK(b5.valid()); BOOST_CHECK(b6.valid()); } BOOST_AUTO_TEST_CASE(testDivision) { amount_t a0; amount_t a1("0.00"); balance_t b0; balance_t b1(4.00); balance_t b2(4UL); balance_t b3("CAD -24"); balance_t b4("EUR 4"); balance_t b5("$2"); balance_t b6; BOOST_CHECK_EQUAL(b1 /= 2.00, amount_t(2.00)); BOOST_CHECK_EQUAL(b2 /= 2L, amount_t(2L)); BOOST_CHECK_EQUAL(b2 /= 2UL, amount_t(1UL)); BOOST_CHECK_EQUAL(b3 /= amount_t("-3 CAD"), amount_t("CAD 8")); BOOST_CHECK_EQUAL(b0 /= 2UL, b0); b6 += b3; b3 += b4; b3 += b5; b3 /= 2L; b6 /= 2L; b4 /= 2L; b5 /= 2L; b6 += b4; b6 += b5; BOOST_CHECK_EQUAL(b3, b6); BOOST_CHECK_THROW(b1 /= a0 , balance_error); BOOST_CHECK_THROW(b1 /= a1 , balance_error); BOOST_CHECK_THROW(b4 /= amount_t("1 CAD") , balance_error); BOOST_CHECK_THROW(b3 /= amount_t("1 CAD") , balance_error); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); BOOST_CHECK(b4.valid()); BOOST_CHECK(b5.valid()); BOOST_CHECK(b6.valid()); } BOOST_AUTO_TEST_CASE(testNegation) { amount_t a1("0.00"); amount_t a2("$ 123"); amount_t a3("EUR 456"); balance_t b0; balance_t b1; balance_t b2; balance_t b3; b1 += a1; b1 += a2; b1 += a3; b2 += -a1; b2 += -a2; b2 += -a3; b3 = -b1; BOOST_CHECK_EQUAL(b0.negated(), b0); BOOST_CHECK_EQUAL(b2, b3); BOOST_CHECK_EQUAL(b2, -b1); BOOST_CHECK_EQUAL(b2.negated(), b1); b2.in_place_negate(); BOOST_CHECK_EQUAL(b2, b1); BOOST_CHECK_EQUAL(b1, -b3); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); } BOOST_AUTO_TEST_CASE(testAbs) { amount_t a1("0.00"); amount_t a2("$ 123"); amount_t a3("EUR 456"); balance_t b0; balance_t b1; balance_t b2; b1 += a1; b1 += a2; b1 += a3; b2 += -a1; b2 += -a2; b2 += -a3; BOOST_CHECK_EQUAL(b0.abs(), b0); BOOST_CHECK_EQUAL(b2.abs(), b1.abs()); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); } BOOST_AUTO_TEST_CASE(testCeiling) { amount_t a1("0.00"); amount_t a2("$ 123.123"); amount_t a3("EUR 456.56"); amount_t a4(-a1); amount_t a5(-a2); amount_t a6(-a3); balance_t b0; balance_t b1; balance_t b2; balance_t b3; balance_t b4; b1 += a1; b1 += a2; b1 += a3; b2 += -a1; b2 += -a2; b2 += -a3; b3 += a1.ceilinged(); b3 += a2.ceilinged(); b3 += a3.ceilinged(); b4 += a4.ceilinged(); b4 += a5.ceilinged(); b4 += a6.ceilinged(); BOOST_CHECK_EQUAL(b0.ceilinged(), b0); BOOST_CHECK_EQUAL(b2.ceilinged(), b4); BOOST_CHECK_EQUAL(b1.ceilinged(), b3); b1.in_place_ceiling(); BOOST_CHECK_EQUAL(b1, b3); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); BOOST_CHECK(b4.valid()); } BOOST_AUTO_TEST_CASE(testFloor) { amount_t a1("0.00"); amount_t a2("$ 123.123"); amount_t a3("EUR 456.56"); amount_t a4(-a1); amount_t a5(-a2); amount_t a6(-a3); balance_t b0; balance_t b1; balance_t b2; balance_t b3; balance_t b4; b1 += a1; b1 += a2; b1 += a3; b2 += -a1; b2 += -a2; b2 += -a3; b3 += a1.floored(); b3 += a2.floored(); b3 += a3.floored(); b4 += a4.floored(); b4 += a5.floored(); b4 += a6.floored(); BOOST_CHECK_EQUAL(b0.floored(), b0); BOOST_CHECK_EQUAL(b2.floored(), b4); BOOST_CHECK_EQUAL(b1.floored(), b3); b1.in_place_floor(); BOOST_CHECK_EQUAL(b1, b3); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); BOOST_CHECK(b4.valid()); } BOOST_AUTO_TEST_CASE(testRound) { amount_t a1("0.00"); amount_t a2("$ 123.123"); amount_t a3("EUR 456.567"); amount_t a4("0.00"); amount_t a5("$ 123.12"); amount_t a6("EUR 456.57"); balance_t b0; balance_t b1; balance_t b2; balance_t b3; balance_t b4; b1 += a1; b1 += a2; b1 += a3; b2 += a4; b2 += a5; b2 += a6; a1.in_place_roundto(2); a2.in_place_roundto(2); a3.in_place_roundto(2); a4.in_place_roundto(2); a5.in_place_roundto(2); a6.in_place_roundto(2); b3 += a1; b3 += a2; b3 += a3; b4 += a4; b4 += a5; b4 += a6; BOOST_CHECK_EQUAL(b0.rounded(), b0); BOOST_CHECK_EQUAL(b2.rounded(), b4); BOOST_CHECK_EQUAL(b1.rounded(), b4); b1.in_place_round(); BOOST_CHECK_EQUAL(b1, b3); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); BOOST_CHECK(b2.valid()); BOOST_CHECK(b3.valid()); BOOST_CHECK(b4.valid()); } BOOST_AUTO_TEST_CASE(testTruth) { amount_t a1("0.00"); amount_t a2("$ 123"); amount_t a3("EUR 456"); balance_t b0; balance_t b1; b1 += a1; b1 += a2; b1 += a3; BOOST_CHECK(!b0); BOOST_CHECK(b1); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); } BOOST_AUTO_TEST_CASE(testForZero) { amount_t a1("0.00"); amount_t a2("$ 123"); amount_t a3("EUR 456"); balance_t b0; balance_t b1; b1 += a1; b1 += a2; b1 += a3; BOOST_CHECK(b0.is_empty()); BOOST_CHECK(b0.is_zero()); BOOST_CHECK(b0.is_realzero()); BOOST_CHECK(!b0.is_nonzero()); BOOST_CHECK(!b1.is_empty()); BOOST_CHECK(!b1.is_zero()); BOOST_CHECK(!b1.is_realzero()); BOOST_CHECK(b1.is_nonzero()); BOOST_CHECK(b0.valid()); BOOST_CHECK(b1.valid()); } BOOST_AUTO_TEST_SUITE_END() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/unit/t_commodity.cc���������������������������������������������������������������0000664�0000000�0000000�00000006165�14411236400�0017560�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK //#define BOOST_TEST_MODULE commodity #include <boost/test/unit_test.hpp> #include <system.hh> #include "amount.h" #include "commodity.h" using namespace ledger; struct commodity_fixture { commodity_fixture() { times_initialize(); amount_t::initialize(); amount_t::stream_fullstrings = true; } ~commodity_fixture() { amount_t::shutdown(); times_shutdown(); } }; BOOST_FIXTURE_TEST_SUITE(commodity, commodity_fixture) BOOST_AUTO_TEST_CASE(testPriceHistory) { #ifndef NOT_FOR_PYTHON datetime_t jan17_05; datetime_t jan17_06; datetime_t jan17_07; datetime_t feb27_07; datetime_t feb28_07; datetime_t feb28_07sbm; datetime_t mar01_07; datetime_t apr15_07; #endif // NOT_FOR_PYTHON jan17_05 = parse_datetime("2005/01/17 00:00:00"); jan17_06 = parse_datetime("2006/01/17 00:00:00"); jan17_07 = parse_datetime("2007/01/17 00:00:00"); feb27_07 = parse_datetime("2007/02/27 18:00:00"); feb28_07 = parse_datetime("2007/02/28 06:00:00"); feb28_07sbm = parse_datetime("2007/02/28 11:59:59"); mar01_07 = parse_datetime("2007/03/01 00:00:00"); apr15_07 = parse_datetime("2007/04/15 13:00:00"); amount_t x0; amount_t x1("100.10 AAPL"); BOOST_CHECK_THROW(x0.value(), amount_error); #ifndef NOT_FOR_PYTHON BOOST_CHECK(! x1.value()); #endif // Commodities cannot be constructed by themselves, since a great deal // of their state depends on how they were seen to be used. commodity_t& aapl(x1.commodity()); aapl.add_price(jan17_07, amount_t("$10.20")); aapl.add_price(feb27_07, amount_t("$13.40")); aapl.add_price(feb28_07, amount_t("$18.33")); aapl.add_price(feb28_07sbm, amount_t("$18.30")); aapl.add_price(mar01_07, amount_t("$19.50")); aapl.add_price(apr15_07, amount_t("$21.22")); aapl.add_price(jan17_05, amount_t("EUR 23.00")); aapl.add_price(jan17_06, amount_t("CAD 25.00")); amount_t one_euro("EUR 1.00"); commodity_t& euro(one_euro.commodity()); euro.add_price(feb27_07, amount_t("CAD 1.40")); euro.add_price(jan17_05, amount_t("$0.78")); amount_t one_cad("CAD 1.00"); commodity_t& cad(one_cad.commodity()); cad.add_price(jan17_06, amount_t("$1.11")); #ifndef NOT_FOR_PYTHON optional<amount_t> amt = x1.value(feb28_07sbm); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(amount_t("$1831.83"), *amt); amt = x1.value(CURRENT_TIME()); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("$2124.12"), amt->to_string()); #ifdef INTEGER_MATH BOOST_CHECK_EQUAL(string("$2124.12"), amt->to_fullstring()); #else BOOST_CHECK_EQUAL(string("$2124.122"), amt->to_fullstring()); #endif amt = x1.value(CURRENT_TIME(), &euro); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("EUR 1787.50"), amt->rounded().to_string()); // Add a newer Euro pricing aapl.add_price(jan17_07, amount_t("EUR 23.00")); amt = x1.value(CURRENT_TIME(), &euro); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("EUR 2302.30"), amt->to_string()); amt = x1.value(CURRENT_TIME(), &cad); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("CAD 3223.22"), amt->to_string()); #endif // NOT_FOR_PYTHON BOOST_CHECK(x1.valid()); } BOOST_AUTO_TEST_SUITE_END() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/unit/t_expr.cc��������������������������������������������������������������������0000664�0000000�0000000�00000027235�14411236400�0016533�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK //#define BOOST_TEST_MODULE expr #include <boost/test/unit_test.hpp> #include <system.hh> #include "expr.h" #include "predicate.h" #include "query.h" #include "op.h" using namespace ledger; struct expr_fixture { expr_fixture() { times_initialize(); amount_t::initialize(); } ~expr_fixture() { amount_t::shutdown(); times_shutdown(); } }; // 1. foo and bar // 2. 'foo and bar' // 3. (foo and bar) // 4. ( foo and bar ) // 5. '( foo and' bar) // 6. =foo and bar // 7. ='foo and bar' // 8. 'expr foo and bar' // 9. expr 'foo and bar' // 10. expr foo and bar // 11. foo and bar or baz // 12. foo and bar | baz // 13. foo and bar |baz // 14. foo and bar| baz // 15. foo and bar|baz // 16. foo 'and bar|baz' BOOST_FIXTURE_TEST_SUITE(expr, expr_fixture) BOOST_AUTO_TEST_CASE(testPredicateTokenizer1) { value_t args; args.push_back(string_value("foo")); args.push_back(string_value("and")); args.push_back(string_value("bar")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer2) { value_t args; args.push_back(string_value("foo and bar")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end(), false); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer3) { value_t args; args.push_back(string_value("(foo")); args.push_back(string_value("and")); args.push_back(string_value("bar)")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::LPAREN, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::RPAREN, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer4) { value_t args; args.push_back(string_value("(")); args.push_back(string_value("foo")); args.push_back(string_value("and")); args.push_back(string_value("bar")); args.push_back(string_value(")")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::LPAREN, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::RPAREN, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer5) { value_t args; args.push_back(string_value("( foo and")); args.push_back(string_value("bar)")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end(), false); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::LPAREN, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::RPAREN, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer6) { value_t args; args.push_back(string_value("=foo")); args.push_back(string_value("and")); args.push_back(string_value("bar")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_EQ, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer7) { value_t args; args.push_back(string_value("=foo and bar")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_EQ, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer8) { value_t args; args.push_back(string_value("expr 'foo and bar'")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end(), false); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_EXPR, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer9) { value_t args; args.push_back(string_value("expr")); args.push_back(string_value("'foo and bar'")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_EXPR, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer10) { value_t args; args.push_back(string_value("expr")); args.push_back(string_value("foo")); args.push_back(string_value("and")); args.push_back(string_value("bar")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_EXPR, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer11) { value_t args; args.push_back(string_value("foo")); args.push_back(string_value("and")); args.push_back(string_value("bar")); args.push_back(string_value("or")); args.push_back(string_value("baz")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_OR, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer12) { value_t args; args.push_back(string_value("foo")); args.push_back(string_value("and")); args.push_back(string_value("bar")); args.push_back(string_value("|")); args.push_back(string_value("baz")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_OR, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer13) { value_t args; args.push_back(string_value("foo")); args.push_back(string_value("and")); args.push_back(string_value("bar")); args.push_back(string_value("|baz")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_OR, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer14) { value_t args; args.push_back(string_value("foo")); args.push_back(string_value("and")); args.push_back(string_value("bar|")); args.push_back(string_value("baz")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_OR, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer15) { value_t args; args.push_back(string_value("foo")); args.push_back(string_value("and")); args.push_back(string_value("bar|baz")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_OR, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_CASE(testPredicateTokenizer16) { value_t args; args.push_back(string_value("foo")); args.push_back(string_value("and bar|baz")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end(), false); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TOK_OR, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); BOOST_CHECK_EQUAL(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } BOOST_AUTO_TEST_SUITE_END() �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/unit/t_format.cc������������������������������������������������������������������0000664�0000000�0000000�00000012760�14411236400�0017042�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK #include <boost/test/unit_test.hpp> #include <system.hh> #include "format.h" using namespace ledger; struct format_fixture { format_fixture() { format_t::default_style = format_t::TRUNCATE_TRAILING; format_t::default_style_changed = false; } ~format_fixture() { format_t::default_style = format_t::TRUNCATE_TRAILING; format_t::default_style_changed = false; } }; BOOST_FIXTURE_TEST_SUITE(format, format_fixture) BOOST_AUTO_TEST_CASE(testTruncateTrailing) { format_t::default_style = format_t::TRUNCATE_TRAILING; unistring str("abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 0, 0), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 99, 0), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 14, 0), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 13, 0), "abcd:1234:A.."); BOOST_CHECK_EQUAL(format_t::truncate(str, 12, 0), "abcd:1234:.."); BOOST_CHECK_EQUAL(format_t::truncate(str, 11, 0), "abcd:1234.."); BOOST_CHECK_EQUAL(format_t::truncate(str, 10, 0), "abcd:123.."); unistring ustr("中文:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 14, 0), "中文:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 13, 0), "中文:中文:..."); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 12, 0), "中文:中文:.."); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 11, 0), "中文:中文.."); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 10, 0), "中文:中..."); } BOOST_AUTO_TEST_CASE(testTruncateMiddle) { format_t::default_style = format_t::TRUNCATE_MIDDLE; unistring str("abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 0, 0), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 99, 0), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 14, 0), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 13, 0), "abcd:..4:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 12, 0), "abcd:..:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 11, 0), "abcd..:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 10, 0), "abcd..ABCD"); unistring ustr("中文:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 14, 0), "中文:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 13, 0), "中文:...:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 12, 0), "中文:..:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 11, 0), "中文..:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 10, 0), "中文..中文"); } BOOST_AUTO_TEST_CASE(testTruncateLeading) { format_t::default_style = format_t::TRUNCATE_LEADING; unistring str("abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 0, 0), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 99, 0), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 14, 0), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 13, 0), "..d:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 12, 0), "..:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 11, 0), "..1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 10, 0), "..234:ABCD"); unistring ustr("中文:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 14, 0), "中文:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 13, 0), "...:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 12, 0), "..:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 11, 0), "..中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 10, 0), "...文:中文"); } BOOST_AUTO_TEST_CASE(testTruncateAbbreviate) { format_t::default_style = format_t::ABBREVIATE; unistring str("abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 0, 2), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 99, 2), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 14, 2), "abcd:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 13, 2), "abc:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 12, 2), "ab:1234:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 11, 2), "ab:123:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 10, 2), "ab:12:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 9, 2), "..12:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 8, 2), "..2:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 7, 2), "..:ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 6, 2), "..ABCD"); BOOST_CHECK_EQUAL(format_t::truncate(str, 5, 2), "..BCD"); unistring ustr("中文:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 14, 2), "中文:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 13, 2), "中.:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 12, 2), "中:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 11, 2), "中:中.:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 10, 2), "中:中:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 9, 2), "..中:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 8, 2), "...:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 14, 1), "中文:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 13, 1), "中.:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 12, 1), "中:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 11, 1), ".:中文:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 10, 1), ".:中.:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 9, 1), ".:中:中文"); BOOST_CHECK_EQUAL(format_t::truncate(ustr, 8, 1), ".:.:中文"); } BOOST_AUTO_TEST_SUITE_END() ����������������ledger-3.3.2/test/unit/t_times.cc�������������������������������������������������������������������0000664�0000000�0000000�00000011201�14411236400�0016660�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE util #include <boost/test/unit_test.hpp> #include <system.hh> #include "utils.h" #include "times.h" using namespace ledger; struct times_fixture { times_fixture() { times_initialize(); } ~times_fixture() { times_shutdown(); } }; BOOST_FIXTURE_TEST_SUITE(times, times_fixture) BOOST_AUTO_TEST_CASE(testConstructors) { #ifndef NOT_FOR_PYTHON std::time_t now = std::time(NULL); struct tm * moment = std::localtime(&now); std::time_t localMoment = std::mktime(moment); #endif // NOT_FOR_PYTHON #ifndef NOT_FOR_PYTHON date_t d0; date_t d1; datetime_t d3; date_t d4; date_t d5; date_t d6; date_t d7; date_t d8; date_t d9; #if 0 date_t d10; date_t d11; date_t d12; date_t d13; date_t d14; datetime_t d15; #endif #endif // NOT_FOR_PYTHON d1 = parse_date("1990/01/01"); #ifndef NOT_FOR_PYTHON d3 = boost::posix_time::from_time_t(localMoment); #endif // NOT_FOR_PYTHON d4 = parse_date("2006/12/25"); d5 = parse_date("12/25"); d6 = parse_date("2006.12.25"); d7 = parse_date("12.25"); d8 = parse_date("2006-12-25"); d9 = parse_date("12-25"); #ifndef NOT_FOR_PYTHON #if 0 d10 = parse_date("tue"); d11 = parse_date("tuesday"); d12 = parse_date("feb"); d13 = parse_date("february"); d14 = parse_date("2006"); d15 = d3; #endif #endif // NOT_FOR_PYTHON #ifndef NOT_FOR_PYTHON BOOST_CHECK(d0.is_not_a_date()); BOOST_CHECK(! d1.is_not_a_date()); BOOST_CHECK(! d4.is_not_a_date()); #endif // NOT_FOR_PYTHON BOOST_CHECK(CURRENT_DATE() > d1); BOOST_CHECK(CURRENT_DATE() > d4); #ifndef NOT_FOR_PYTHON #if 0 BOOST_CHECK_EQUAL(d3, d15); #endif #endif // NOT_FOR_PYTHON BOOST_CHECK_EQUAL(d4, d6); BOOST_CHECK_EQUAL(d4, d8); BOOST_CHECK_EQUAL(d5, d7); BOOST_CHECK_EQUAL(d5, d9); #ifndef NOT_FOR_PYTHON #if 0 BOOST_CHECK_EQUAL(d10, d11); BOOST_CHECK_EQUAL(d12, d13); BOOST_CHECK_THROW(parse_date("2007/02/29"), boost::gregorian::bad_day_of_month); //BOOST_CHECK_THROW(parse_date("2007/13/01"), datetime_error); //BOOST_CHECK_THROW(parse_date("2007/00/01"), datetime_error); BOOST_CHECK_THROW(parse_date("2007/01/00"), boost::gregorian::bad_day_of_month); //BOOST_CHECK_THROW(parse_date("2007/00/00"), boost::gregorian::bad_day_of_month); //BOOST_CHECK_THROW(parse_date("2007/05/32"), boost::gregorian::bad_day_of_month); BOOST_CHECK_THROW(parse_date("2006x/12/25"), datetime_error); BOOST_CHECK_THROW(parse_date("2006/12x/25"), datetime_error); BOOST_CHECK_THROW(parse_date("2006/12/25x"), datetime_error); BOOST_CHECK_THROW(parse_date("feb/12/25"), datetime_error); BOOST_CHECK_THROW(parse_date("2006/mon/25"), datetime_error); BOOST_CHECK_THROW(parse_date("2006/12/web"), datetime_error); BOOST_CHECK_THROW(parse_date("12*25"), datetime_error); BOOST_CHECK_THROW(parse_date("tuf"), datetime_error); BOOST_CHECK_THROW(parse_date("tufsday"), datetime_error); BOOST_CHECK_THROW(parse_date("fec"), datetime_error); BOOST_CHECK_THROW(parse_date("fecruary"), datetime_error); BOOST_CHECK_THROW(parse_date("207x"), datetime_error); BOOST_CHECK_THROW(parse_date("hello"), datetime_error); d1 = parse_date("2002-02-02"); d1 = parse_date("2002/02/02"); d1 = parse_date("2002.02.02"); d1 = parse_date("02-02-2002"); d1 = parse_date("02/02/2002"); d1 = parse_date("02.02.2002"); d1 = parse_date("02-02-02"); d1 = parse_date("02/02/02"); d1 = parse_date("02.02.02"); d1 = parse_date("02-02"); d1 = parse_date("02/02"); d1 = parse_date("02.02"); d1 = parse_date("20020202"); d1 = parse_date("20020202T023318"); d1 = parse_date("20020202T023318-0700"); d1 = parse_date("20020202T023318-0100"); d1 = parse_date("02-Feb-2002"); d1 = parse_date("2002-Feb-02"); d1 = parse_date("02 Feb 2002"); d1 = parse_date("02-Feb-2002"); d1 = parse_date("02 February 2002"); d1 = parse_date("02-February-2002"); d1 = parse_date("2002 Feb 02"); d1 = parse_date("2002-Feb-02"); d1 = parse_date("2002 February 02"); d1 = parse_date("2002-February-02"); d1 = parse_date("02 Feb"); d1 = parse_date("02-Feb"); d1 = parse_date("02 February"); d1 = parse_date("02-February"); d1 = parse_date("Feb 02"); d1 = parse_date("Feb-02"); d1 = parse_date("February 02"); d1 = parse_date("February-02"); d1 = parse_date("Feb 02, 2002"); d1 = parse_date("February 02, 2002"); d1 = parse_date("2002-02-02 12:00:00"); d1 = parse_date("2002-02-02 12:00:00 AM"); d1 = parse_date("2002-02-02 12:00 AM"); d1 = parse_date("2002-02-02 12:00AM"); d1 = parse_date("2002-02-02 12p"); d1 = parse_date("2002-02-02 12a"); BOOST_CHECK(d1.valid()); #endif // NOT_FOR_PYTHON #endif } BOOST_AUTO_TEST_SUITE_END() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/test/unit/t_value.cc�������������������������������������������������������������������0000664�0000000�0000000�00000044326�14411236400�0016671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK #include <boost/test/unit_test.hpp> #include <system.hh> #include "value.h" #if defined(_WIN32) || defined(__CYGWIN__) #include "strptime.h" #endif using namespace ledger; struct value_fixture { value_fixture() { times_initialize(); amount_t::initialize(); value_t::initialize(); // Cause the display precision for dollars to be initialized to 2. amount_t x1("$1.00"); BOOST_CHECK(x1); amount_t::stream_fullstrings = true; // make reports from UnitTests accurate } ~value_fixture() { amount_t::stream_fullstrings = false; amount_t::shutdown(); times_shutdown(); value_t::shutdown(); } }; BOOST_FIXTURE_TEST_SUITE(value, value_fixture) BOOST_AUTO_TEST_CASE(testConstructors) { value_t::sequence_t s1; value_t v1; value_t v2(true); value_t v3(boost::posix_time::from_time_t(time_t(NULL))); value_t v4(date_t(parse_date("2014/08/14"))); value_t v5(2L); value_t v6(4UL); value_t v7(1.00); value_t v8(amount_t("4 GBP")); value_t v9(balance_t("3 EUR")); value_t v10(mask_t("regex")); value_t v11(s1); value_t v12(string("$1")); value_t v13("2 CAD"); value_t v14("comment", true); value_t v15(string("tag"), true); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); } BOOST_AUTO_TEST_CASE(testAssignment) { value_t::sequence_t s1; value_t v1; value_t v2 = true; value_t v3 = boost::posix_time::from_time_t(time_t(NULL)); value_t v4 = date_t(parse_date("2014/08/14")); value_t v5 = -2L; value_t v6 = 4UL; value_t v7 = 1.00; value_t v8 = amount_t("4 GBP"); value_t v9 = balance_t("3 EUR"); value_t v10 = mask_t("regex"); value_t v11 = s1; value_t v12 = value_t(string("$1")); value_t v13 = value_t("2 CAD"); value_t v14 = value_t("comment", true); value_t v15 = value_t(string("tag"), true); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); } BOOST_AUTO_TEST_CASE(testEquality) { struct tm localtime; strptime("10 February 2010", "%d %b %Y", &localtime); value_t::sequence_t s1; value_t v1; value_t v2(true); value_t v3(boost::posix_time::ptime_from_tm(localtime)); value_t v4(date_t(parse_date("2014/08/14"))); value_t v5(2L); value_t v6(2UL); value_t v7(1.00); value_t v8(amount_t("4 GBP")); value_t v9(balance_t("4 GBP")); value_t v10(mask_t("regex")); value_t v11(s1); value_t v12(string("$1")); value_t v13("2 CAD"); value_t v14("comment", true); value_t v15(string("comment"), true); value_t v16; BOOST_CHECK_EQUAL(v1, value_t()); BOOST_CHECK_EQUAL(v2, value_t(true)); BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::ptime_from_tm(localtime))); BOOST_CHECK(!(v4 == value_t(date_t(parse_date("2014/08/15"))))); value_t v19(amount_t("2")); value_t v20(balance_t("2")); BOOST_CHECK_EQUAL(v5, v6); BOOST_CHECK_EQUAL(v5, v19); BOOST_CHECK_EQUAL(v5, v20); BOOST_CHECK(v19 == v5); BOOST_CHECK(v19 == v20); BOOST_CHECK(v19 == value_t(amount_t("2"))); BOOST_CHECK(v20 == v5); BOOST_CHECK(v20 == v19); BOOST_CHECK(v20 == value_t(balance_t(2L))); BOOST_CHECK(v14 == v15); BOOST_CHECK(v10 == value_t(mask_t("regex"))); BOOST_CHECK(v11 == value_t(s1)); BOOST_CHECK_THROW(v8 == v10, value_error); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); BOOST_CHECK(v19.valid()); BOOST_CHECK(v20.valid()); } BOOST_AUTO_TEST_CASE(testSequence) { value_t::sequence_t s1; value_t v1(s1); BOOST_CHECK(v1.is_sequence()); v1.push_back(value_t(2L)); v1.push_back(value_t("3 GBP")); value_t v2("3 GBP"); value_t seq(v1); const value_t v3(seq); value_t::sequence_t::iterator i = std::find(seq.begin(), seq.end(), v2); if (i != seq.end()) BOOST_CHECK(v2 == *i); value_t::sequence_t::const_iterator j = std::find(v3.begin(), v3.end(), v2); if (j != v3.end()) BOOST_CHECK(v2 == *j); BOOST_CHECK(v2 == seq[1]); BOOST_CHECK(v2 == v3[1]); v1.pop_back(); v1.pop_back(); v1.push_front(v2); v1.push_front(value_t(2L)); BOOST_CHECK(v2 == v1[1]); BOOST_CHECK(seq == v1); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(seq.valid()); } BOOST_AUTO_TEST_CASE(testAddition) { struct tm localtime; strptime("10 February 2010 00:00:00", "%d %b %Y %H:%M:%S", &localtime); value_t::sequence_t s1; value_t v1; value_t v2(true); value_t v3(boost::posix_time::ptime_from_tm(localtime)); value_t v4(date_t(parse_date("2014/08/14"))); value_t v5(2L); value_t v6(2UL); value_t v7(1.00); value_t v8(amount_t("4 GBP")); value_t v9(balance_t("4 GBP")); value_t v10(mask_t("regex")); value_t v11(s1); value_t v12(string("$1")); value_t v13("2 CAD"); value_t v14("comment", true); value_t v15(string("comment"), true); value_t v16(amount_t("2")); v14 += v15; BOOST_CHECK_EQUAL(v14, value_t(string("commentcomment"), true)); v14 += v12; BOOST_CHECK_EQUAL(v14, value_t(string("commentcomment$1.00"), true)); strptime("10 February 2010 00:00:00", "%d %b %Y %H:%M:%S", &localtime); BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::ptime_from_tm(localtime))); v3 += value_t(2L); strptime("10 February 2010 00:00:02", "%d %b %Y %H:%M:%S", &localtime); BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::ptime_from_tm(localtime))); v3 += value_t(amount_t("2")); strptime("10 February 2010 00:00:04", "%d %b %Y %H:%M:%S", &localtime); BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::ptime_from_tm(localtime))); v4 += value_t(2L); BOOST_CHECK_EQUAL(v4, value_t(date_t(parse_date("2014/08/16")))); v4 += value_t(amount_t("2")); BOOST_CHECK_EQUAL(v4, value_t(date_t(parse_date("2014/08/18")))); v5 += value_t(2L); BOOST_CHECK_EQUAL(v5, value_t(4L)); v5 += value_t(amount_t("2")); BOOST_CHECK_EQUAL(v5, value_t(amount_t("6"))); v5 += v8; v16 += value_t(2L); v16 += value_t(amount_t("2")); v16 += v8; BOOST_CHECK_EQUAL(v5, v16); v8 += value_t("6"); BOOST_CHECK_EQUAL(v8, v16); value_t v17(6L); v17 += value_t(amount_t("4 GBP")); BOOST_CHECK_EQUAL(v8, v17); value_t v18(6L); v18 += v9; value_t v19(amount_t("6")); v19 += v9; BOOST_CHECK_EQUAL(v18, v19); v9 += value_t(2L); v9 += value_t(amount_t("4")); v9 += v19; v18 += v19; BOOST_CHECK_EQUAL(v9, v18); value_t v20(s1); v11 += value_t(2L); v11 += value_t("4 GBP"); BOOST_CHECK_THROW(v11 += v20,value_error); BOOST_CHECK_THROW(v10 += v8, value_error); v20 += value_t(2L); v20 += value_t("4 GBP"); BOOST_CHECK_EQUAL(v11, v20); v11 += v20; v20 += v20; BOOST_CHECK_EQUAL(v11, v20); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); BOOST_CHECK(v16.valid()); BOOST_CHECK(v17.valid()); BOOST_CHECK(v18.valid()); BOOST_CHECK(v19.valid()); BOOST_CHECK(v20.valid()); } BOOST_AUTO_TEST_CASE(testSubtraction) { struct tm localtime; strptime("10 February 2010 00:00:04", "%d %b %Y %H:%M:%S", &localtime); value_t::sequence_t s1; value_t v1; value_t v2(true); value_t v3(boost::posix_time::ptime_from_tm(localtime)); value_t v4(date_t(parse_date("2014/08/18"))); value_t v5(6L); value_t v6(6UL); value_t v7(1.00); value_t v8(amount_t("4 GBP")); value_t v9(balance_t("4 GBP")); value_t v10(mask_t("regex")); value_t v11(s1); value_t v12(string("$1")); value_t v13("2 CAD"); value_t v14("comment", true); value_t v15(string("comment"), true); value_t v16(amount_t("6")); v3 -= value_t(2L); strptime("10 February 2010 00:00:02", "%d %b %Y %H:%M:%S", &localtime); BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::ptime_from_tm(localtime))); v3 -= value_t(amount_t("2")); strptime("10 February 2010 00:00:00", "%d %b %Y %H:%M:%S", &localtime); BOOST_CHECK_EQUAL(v3, value_t(boost::posix_time::ptime_from_tm(localtime))); v4 -= value_t(2L); BOOST_CHECK_EQUAL(v4, value_t(date_t(parse_date("2014/08/16")))); v4 -= value_t(amount_t("2")); BOOST_CHECK_EQUAL(v4, value_t(date_t(parse_date("2014/08/14")))); v5 -= value_t(2L); BOOST_CHECK_EQUAL(v5, value_t(4L)); v5 -= value_t(amount_t("2")); BOOST_CHECK_EQUAL(v5, value_t(amount_t("2"))); v5 -= v8; v16 -= value_t(2L); v16 -= value_t(amount_t("2")); v16 -= v8; BOOST_CHECK_EQUAL(v5, v16); v8 -= value_t("2"); BOOST_CHECK_EQUAL(-v8, v16); value_t v18(6L); v18 -= v9; value_t v19(amount_t("6")); v19 -= v9; BOOST_CHECK_EQUAL(v18, v19); v9 -= value_t(-2L); v9 -= value_t(amount_t("-10")); v9 -= value_t(amount_t("12 GBP")); v9 -= v19; BOOST_CHECK_EQUAL(v9, v18); v18 -=v19; BOOST_CHECK_EQUAL(v18, value_t("0")); value_t v20(s1); value_t v21(2L); value_t v22("4 GBP"); v11.push_back(v21); v11.push_back(v22); BOOST_CHECK_THROW(v11 -= v20,value_error); BOOST_CHECK_THROW(v10 -= v8, value_error); v20.push_back(v21); v20.push_back(v22); v11 -= v20; value_t v23(s1); v23.push_back(value_t(0L)); v23.push_back(value_t("0")); BOOST_CHECK_EQUAL(v11, v23); v20 -= v21; v20 -= v22; BOOST_CHECK_EQUAL(v20, value_t(s1)); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); BOOST_CHECK(v16.valid()); BOOST_CHECK(v18.valid()); BOOST_CHECK(v19.valid()); BOOST_CHECK(v20.valid()); } BOOST_AUTO_TEST_CASE(testMultiplication) { struct tm localtime; strptime("10 February 2010 00:00:00", "%d %b %Y %H:%M:%S", &localtime); value_t::sequence_t s1; value_t v1; value_t v2(true); value_t v3(boost::posix_time::ptime_from_tm(localtime)); value_t v4(date_t(parse_date("2014/08/14"))); value_t v5(2L); value_t v6(2UL); value_t v7(1.00); value_t v8(amount_t("4 GBP")); value_t v9(balance_t("4 GBP")); value_t v10(mask_t("regex")); value_t v11(s1); value_t v12(string("$1")); value_t v13("2 CAD"); value_t v14("comment", true); value_t v15(string("comment"), true); value_t v16(amount_t("2")); v14 *= value_t(2L); BOOST_CHECK_EQUAL(v14, value_t(string("commentcomment"), true)); v5 *= value_t(2L); BOOST_CHECK_EQUAL(v5, value_t(4L)); v5 *= value_t(amount_t("2")); BOOST_CHECK_EQUAL(v5, value_t(amount_t("8"))); v16 *= value_t(2L); v16 *= value_t(amount_t("2")); BOOST_CHECK_EQUAL(v5, v16); v8 *= v9; BOOST_CHECK_EQUAL(v8, value_t("16 GBP")); value_t v17(v9); v9 *= value_t(2L); BOOST_CHECK_EQUAL(v9, value_t("8 GBP")); v17 += value_t(2L); v17 *= value_t(2L); value_t v18("8 GBP"); v18 += value_t(4L); BOOST_CHECK_EQUAL(v17, v18); value_t v20(s1); v11.push_back(value_t(2L)); v11.push_back(value_t("2 GBP")); v20.push_back(value_t(4L)); v20.push_back(value_t("4 GBP")); v11 *= value_t(2L); BOOST_CHECK_EQUAL(v11 ,v20); BOOST_CHECK_THROW(v10 *= v8, value_error); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); BOOST_CHECK(v16.valid()); BOOST_CHECK(v17.valid()); BOOST_CHECK(v18.valid()); } BOOST_AUTO_TEST_CASE(testDivision) { struct tm localtime; strptime("10 February 2010 00:00:00", "%d %b %Y %H:%M:%S", &localtime); value_t::sequence_t s1; value_t v1; value_t v2(true); value_t v3(boost::posix_time::ptime_from_tm(localtime)); value_t v4(date_t(parse_date("2014/08/14"))); value_t v5(8L); value_t v6(2UL); value_t v7(1.00); value_t v8(amount_t("4 GBP")); value_t v9(balance_t("4 GBP")); value_t v10(mask_t("regex")); value_t v11(s1); value_t v12(string("$1")); value_t v13("2 CAD"); value_t v14("comment", true); value_t v15(string("comment"), true); value_t v16(amount_t("8")); v5 /= value_t(2L); BOOST_CHECK_EQUAL(v5, value_t(4L)); v5 /= value_t(amount_t("8")); BOOST_CHECK_EQUAL(v5, value_t(amount_t("2"))); v16 /= value_t(2L); v16 /= value_t(amount_t("2")); BOOST_CHECK_EQUAL(v5, v16); v8 /= v9; v8 /= value_t(balance_t(2L)); BOOST_CHECK_EQUAL(v8, value_t("0.5 GBP")); value_t v17(v9); v9 /= value_t(2L); BOOST_CHECK_EQUAL(v9, value_t("2 GBP")); v17 /= value_t("2 GBP"); v17 /= value_t("2"); BOOST_CHECK_EQUAL(v17, value_t(balance_t("1 GBP"))); BOOST_CHECK_THROW(v10 /= v8, value_error); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); BOOST_CHECK(v16.valid()); BOOST_CHECK(v17.valid()); } BOOST_AUTO_TEST_CASE(testType) { value_t::sequence_t s1; value_t v1; value_t v2(true); value_t v3(boost::posix_time::from_time_t(time_t(NULL))); value_t v4(date_t(parse_date("2014/08/14"))); value_t v5(2L); value_t v6(4UL); value_t v7(1.00); value_t v8(amount_t("4 GBP")); value_t v9(balance_t("3 EUR")); value_t v10(mask_t("regex")); value_t v11(s1); value_t v12(string("$1")); value_t v13("2 CAD"); value_t v14("comment", true); value_t v15(string("tag"), true); BOOST_CHECK(v1.is_null()); BOOST_CHECK(v2.is_boolean()); BOOST_CHECK(v3.is_datetime()); BOOST_CHECK(v4.is_date()); BOOST_CHECK(v5.is_long()); BOOST_CHECK(v6.is_amount()); BOOST_CHECK(v7.is_amount()); BOOST_CHECK(v8.is_amount()); BOOST_CHECK(v9.is_balance()); BOOST_CHECK(v10.is_mask()); BOOST_CHECK(v11.is_sequence()); BOOST_CHECK(v12.is_amount()); BOOST_CHECK(v13.is_amount()); BOOST_CHECK(v14.is_string()); BOOST_CHECK(v15.is_string()); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); } BOOST_AUTO_TEST_CASE(testForZero) { value_t::sequence_t s1; value_t v1; value_t v2(true); value_t v3(boost::posix_time::from_time_t(time_t(NULL))); value_t v4(date_t(0)); value_t v5(2L); value_t v6(0UL); value_t v7(1.00); value_t v8(amount_t("4 GBP")); value_t v9(balance_t("0")); value_t v10(mask_t("")); value_t v11(s1); value_t v12(string("$1")); value_t v13("2 CAD"); value_t v14("comment", true); value_t v15(string(""), true); BOOST_CHECK(v1.is_null()); BOOST_CHECK(v2.is_nonzero()); BOOST_CHECK(!v3.is_zero()); BOOST_CHECK(v4.is_nonzero()); BOOST_CHECK(v5.is_nonzero()); BOOST_CHECK(v6.is_realzero()); BOOST_CHECK(v7.is_nonzero()); BOOST_CHECK(v8.is_nonzero()); BOOST_CHECK(v9.is_zero()); BOOST_CHECK_THROW(v10.is_zero(), value_error); BOOST_CHECK(v11.is_zero()); BOOST_CHECK(v12.is_nonzero()); BOOST_CHECK(v13.is_nonzero()); BOOST_CHECK(v14.is_nonzero()); BOOST_CHECK(v15.is_zero()); v11.push_back(v6); BOOST_CHECK(v11.is_nonzero()); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); } BOOST_AUTO_TEST_CASE(testNegation) { value_t::sequence_t s1; value_t v1; value_t v2(true); value_t v3(boost::posix_time::from_time_t(time_t(NULL))); value_t v4(date_t(parse_date("2014/08/09"))); value_t v5(2L); value_t v6(0UL); value_t v7(1.00); value_t v8(amount_t("4 GBP")); value_t v9(balance_t("4 GBP")); value_t v10(mask_t("")); value_t v11(s1); value_t v12(string("$1")); value_t v13("$-1"); value_t v14("comment", true); value_t v15(string("comment"), true); BOOST_CHECK_THROW(v1.negated(), value_error); BOOST_CHECK_EQUAL(v2.negated(), value_t(false)); v5.in_place_negate(); BOOST_CHECK_EQUAL(v5, value_t(-2L)); v8.in_place_negate(); v9.in_place_negate(); BOOST_CHECK_EQUAL(v8, v9); BOOST_CHECK_THROW(v10.negated(), value_error); BOOST_CHECK_EQUAL(-v12, v13); BOOST_CHECK_THROW(-v14, value_error); BOOST_CHECK(v1.valid()); BOOST_CHECK(v2.valid()); BOOST_CHECK(v3.valid()); BOOST_CHECK(v4.valid()); BOOST_CHECK(v5.valid()); BOOST_CHECK(v6.valid()); BOOST_CHECK(v7.valid()); BOOST_CHECK(v8.valid()); BOOST_CHECK(v9.valid()); BOOST_CHECK(v10.valid()); BOOST_CHECK(v11.valid()); BOOST_CHECK(v12.valid()); BOOST_CHECK(v13.valid()); BOOST_CHECK(v14.valid()); BOOST_CHECK(v15.valid()); } BOOST_AUTO_TEST_SUITE_END() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/���������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14411236400�0014114�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/average��������������������������������������������������������������������������0000775�0000000�0000000�00000000643�14411236400�0015457�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import getopt import time import string import sys import os count = 10 opts, args = getopt.getopt(sys.argv[1:], "n:") for opt in opts: if opt[0] == "-n": count = int(opt[1]) length = 0.0 i = 0 while i < count: begin = time.time() cmd = '"' + string.join(args, '" "') + '"'; os.system(cmd) length += time.time() - begin i += 1 print >> sys.stderr, length / count ���������������������������������������������������������������������������������������������ledger-3.3.2/tools/build.sh�������������������������������������������������������������������������0000775�0000000�0000000�00000001002�14411236400�0015543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh flavor=$1 shift 1 JOBS=-j$(sysctl -n hw.activecpu) #OPTIONS="$flavor --debug --python --ninja --doxygen $JOBS" OPTIONS="$flavor --debug --ninja $JOBS" time ( \ cd ~/src/ledger ; \ PATH=/usr/local/bin:/opt/local/bin:$PATH \ nice -n 20 ./acprep $OPTIONS make $JOBS "$@" && \ PATH=/usr/local/bin:/opt/local/bin:$PATH \ nice -n 20 ./acprep $OPTIONS check $JOBS "$@" \ ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/cleanup.sh�����������������������������������������������������������������������0000775�0000000�0000000�00000000134�14411236400�0016100�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#/bin/bash mv ledger3.texi keep-ledger3.texi rm ledger3.* mv keep-ledger3.texi ledger3.texi������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/excludes�������������������������������������������������������������������������0000664�0000000�0000000�00000000667�14411236400�0015664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������*.la *.lo *.o .deps/ .git* .git/ .libs/ Makefile Makefile.in RegressionTests aclocal.m4 autom4te.cache/ build/ config.guess config.h config.h.in config.h.in~ config.log config.rpath config.status config.sub configure data_tests depcomp doc/.dirstamp doc/ledger.info expr_tests install-sh intl/ ledger ledger.so lib/utfcpp/ libtool ltmain.sh m4/ make.sh math_tests missing po/ report_tests src/system.hh.gch stamp-h1 texinfo.tex util_tests �������������������������������������������������������������������������ledger-3.3.2/tools/gendocs.sh�����������������������������������������������������������������������0000775�0000000�0000000�00000001740�14411236400�0016077�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # By default US Letter is used as the PDF papersize. # For those preferring other dimensions add a4 or small # as a commandline argument to this script to create a # DIN A4 or smallbook version of the PDF. case $1 in a4*|afour*) papersize='--texinfo=@afourpaper';; small*) papersize='--texinfo=@smallbook';; *) papersize='';; # US Letter is texinfo default esac # Use keg-only Mac Hombrew texinfo if installed. # Since texi2pdf is a shell script itself executing texi2dvi # PATH is prepended with the path to correct texinfo scripts. if [ $(uname -s) = 'Darwin' ]; then brew list texinfo >/dev/null 2>&1 \ && export PATH="$(brew --prefix texinfo)/bin:$PATH" fi echo "===================================== Making Info..." makeinfo ledger3.texi echo "===================================== Making HTML..." makeinfo --html --no-split ledger3.texi echo "===================================== Making PDF..." texi2pdf --quiet --batch ${papersize} ledger3.texi ��������������������������������ledger-3.3.2/tools/genuuid��������������������������������������������������������������������������0000775�0000000�0000000�00000001506�14411236400�0015504�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python import re import sys def scan_path(path): bug = uuid = None with open(path, 'r') as fd: for line in fd: match = re.match('\*', line) if match: bug = uuid = None match = re.search('\[\[bug:([0-9]+)\]\[#[0-9]+\]\]', line) if match: bug = match.group(1) elif bug: match = re.search(':ID:\s+(.+?)\s*$', line) if match: uuid = match.group(1) print "UPDATE bugs SET cf_uuid='%s' WHERE bug_id=%s;" % (uuid, bug) scan_path('/Users/johnw/src/ledger/plan/TODO') scan_path('/Users/johnw/src/ledger/plan/TODO-3.0') scan_path('/Users/johnw/src/ledger/plan/TODO-2.6.2') scan_path('/Users/johnw/src/ledger/plan/TODO-2.6.1') ### genuuid ends here ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/nix-build.sh���������������������������������������������������������������������0000775�0000000�0000000�00000001271�14411236400�0016347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh flavor=$1 shift 1 JOBS=-j$(sysctl -n hw.activecpu) OPTIONS="$flavor --debug --python --ninja $JOBS" #OPTIONS="$flavor --debug --python --ninja --doxygen $JOBS" time nice -n 20 nix-shell -p \ cmake boost gmp mpfr libedit python texinfo gnused \ ninja doxygen \ --command "./acprep $OPTIONS make $JOBS $@" && \ time nice -n 20 nix-shell -p \ cmake boost gmp mpfr libedit python texinfo gnused \ ninja doxygen \ --command "./acprep $OPTIONS check $JOBS $@" \ \ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/pre-commit�����������������������������������������������������������������������0000775�0000000�0000000�00000004211�14411236400�0016114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # Exit with status 1 if any command below fails set -e # Exit if it's not a branch we're interested in being thorough about if echo $(git rev-parse --symbolic-full-name HEAD) | \ egrep -q '^refs/heads/(t/)'; then exit 0 fi # These are the locations I keep my temporary source and build trees in PRODUCTS=$(./acprep products) # generates a build directory name such as # ~/Products/ledger if echo $PRODUCTS | grep -qv ledger; then PRODUCTS=$PRODUCTS/ledger fi TMPDIR=$PRODUCTS/pre-commit MIRROR=$PRODUCTS/pre-commit-mirror # Checkout a copy of the current index into MIRROR git checkout-index --prefix=$MIRROR/ -af # Remove files from MIRROR which are no longer present in the index git diff-index --cached --name-only --diff-filter=D -z HEAD | \ (cd $MIRROR && xargs -0 rm -f --) # Copy only _changed files_ from MIRROR to TMPDIR, without copying # timestamps. This includes copying over new files, and deleting # removed ones. This way, "make check" will only rebuild what is # necessary to validate the commit. rsync -rlpgoDOc --delete --exclude-from=tools/excludes $MIRROR/ $TMPDIR/ # Everything else happens in the temporary build tree if [ ! -f $TMPDIR/lib/utfcpp/source/utf8.h ]; then rsync -a --delete lib/utfcpp/ $TMPDIR/lib/utfcpp/ fi cd $TMPDIR # Make sure there is a current Makefile. Regeneration of Makefile # happens automatically, but if myacprep or acprep changes, we want to # regenerate everything manually. If the user doesn't have acprep, look # for other common autoconf-related script files. if [ ! -f Makefile -o \ Makefile.in -nt Makefile -o \ configure -nt Makefile -o \ tools/Makefile.am -nt Makefile.in -o \ tools/configure.ac -nt configure -o \ \( -f acprep -a acprep -nt Makefile \) ] then if [ -f acprep ]; then echo Will run acprep in a moment elif [ -f autogen.sh ]; then sh autogen.sh && ./configure else autoreconf && ./configure fi fi # Finally, (re)build this proposed source tree and see if it passes # muster. if [ -f acprep ]; then nice -n 20 ./acprep default --warn make check else nice -n 20 make check fi exit 0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/prepare-commit-msg���������������������������������������������������������������0000775�0000000�0000000�00000002065�14411236400�0017555�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # Prepare git commit message: # - Add [ci skip] if the changes seem irrelevant for continuous integration # Add [ci skip] to the commit message unless there are changes to files # that are relevant for testing such as src/*, test/*, ledger3.texi, ... add_ci_skip () { pattern="$1"; shift source="$1" # Don't add [ci skip] if it's already in the commit message source grep '\[ci skip\]' "$source" >/dev/null 2>&1 [ $? -eq 0 ] && return if [ $(git diff --cached --name-only | grep --count "$pattern") -eq 0 ]; then tempfile=$(mktemp "${0}.XXXXXX") cat - "$1" <<EOF > "$tempfile" # It seems the changes to be committed are irrelevant for the continuous # integration, therefore it will be skipped for this commit. # # If you still want continuous integration to run for this commit # comment or remove the next line. [ci skip] EOF mv "$tempfile" "$source" fi } ## MAIN file="$1" source="$2" # Skip merge commits [ "$source" = "merge" ] && exit 0 add_ci_skip '\(^src\|^test\|^doc/ledger3.texi\|^\.travis.yml\|CMakeLists.txt\)' "$file" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/proof����������������������������������������������������������������������������0000775�0000000�0000000�00000003470�14411236400�0015173�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash set -e ledger_proof() { SRC="$1" DEST="$2" LOGDIR="$3" cd "$SRC" VERSION=$(git describe --all --long) if [[ -f $DEST/last-proofed && $(< $DEST/last-proofed) = $VERSION ]]; then echo "No need to run tools/proof again" exit 0 fi sudo rm -fr $DEST/ledger-proof date > $LOGDIR/ledger-proof.log time nice -n 20 \ ./acprep --debug --doxygen --compiler=g++-4.7 proof -j16 2>&1 | \ tee -a $LOGDIR/ledger-proof-g++-4.7.log time nice -n 20 \ ./acprep --debug --doxygen --python --compiler=g++-4.7 proof -j16 2>&1 | \ tee -a $LOGDIR/ledger-proof-g++-4.7-python.log time nice -n 20 \ ./acprep --debug --doxygen --compiler=clang-3.1 proof -j16 2>&1 | \ tee -a $LOGDIR/ledger-proof-clang-3.1.log time nice -n 20 \ ./acprep --debug --doxygen --python --compiler=clang-3.1 proof -j16 2>&1 | \ tee -a $LOGDIR/ledger-proof-clang-3.1-python.log if egrep -q '(ERROR|CRITICAL)' $LOGDIR/ledger-proof.log; then mutt -a $LOGDIR/ledger-proof.log \ -s '[ledger] Proof build FAILED' johnw@newartisans.com <<EOF Ledger proof build FAILED, at commit $VERSION. EOF if [[ "$1" = "--alert" ]]; then notify "Ledger proof build FAILED" else echo "Ledger proof build FAILED" exit 1 fi else echo $VERSION > $DEST/last-proofed cd $DEST/ledger-proof-python-g++-4.7/debug; make docs cd $DEST/ledger-proof-python-g++-4.7/gcov; make report mutt -s '[ledger] Proof build succeeded' johnw@newartisans.com <<EOF Ledger proof build succeeded! at commit $VERSION. EOF echo "Ledger proof build succeeded" fi } ledger_proof ${1:-$HOME/src/ledger} ${2:-$HOME/Products} ${3:-$HOME/Library/Logs} exit 0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/push�����������������������������������������������������������������������������0000775�0000000�0000000�00000002110�14411236400�0015013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh set -e ACPREP="./acprep --universal -j16 --warn opt" (cd plan/data; git push) (cd plan; git commit -a -m "Update TODO files" && git push) git checkout next perl -i -pe "s/([-abgrc][0-9]*)?\\]\\)/-$(date +%Y%m%d)])/;" version.m4 git add version.m4 echo git commit -m "v$(cat version.m4 | sed 's/.*\[//' | sed 's/\].*//')" git checkout master git merge --no-ff next git checkout next git rebase master git push git checkout master $ACPREP upload $ACPREP make dist scp ~/Products/ledger/opt/ledger-*.tar.* jw:/srv/ftp/pub/ledger openssl md5 *.dmg* ~/Products/ledger/opt/ledger-*.tar.* > build/CHECKSUMS.txt openssl sha1 *.dmg* ~/Products/ledger/opt/ledger-*.tar.* >> build/CHECKSUMS.txt openssl rmd160 *.dmg* ~/Products/ledger/opt/ledger-*.tar.* >> build/CHECKSUMS.txt perl -i -pe 's/\/.*\///;' build/CHECKSUMS.txt scp build/CHECKSUMS.txt jw:/srv/ftp/pub/ledger rsync -az --delete ~/Products/ledger-proof/gcov/doc/report/ jw:/srv/ftp/pub/ledger/lcov/ $ACPREP make speedtest 2>&1 | tee build/last-speed.txt mv *.dmg* ~/Products/ledger/opt/ledger-*.tar.* build git checkout next ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/rename.sh������������������������������������������������������������������������0000775�0000000�0000000�00000004770�14411236400�0015732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/zsh git reset --hard rm -f src/system.hh.gch ledger for file in *(.) contrib/*(.) doc/*(.) plan/*(.) python/*(.) src/*(.) test/*(.) test/*/*(.) tools/*(.) do echo Renaming items in $file ... perl -i -pe 's/xact/fazole/g;' $file 2> /dev/null perl -i -pe 's/XACT/FAZOLE/g;' $file 2> /dev/null perl -i -pe 's/Xact/Fazole/g;' $file 2> /dev/null perl -i -pe 's/xacts/fazoles/g;' $file 2> /dev/null perl -i -pe 's/XACTS/FAZOLES/g;' $file 2> /dev/null perl -i -pe 's/Xacts/Fazoles/g;' $file 2> /dev/null perl -i -pe 's/transaction/brazole/g;' $file 2> /dev/null perl -i -pe 's/TRANSACTION/BRAZOLE/g;' $file 2> /dev/null perl -i -pe 's/Transaction/Brazole/g;' $file 2> /dev/null perl -i -pe 's/transactions/brazoles/g;' $file 2> /dev/null perl -i -pe 's/TRANSACTIONS/BRAZOLES/g;' $file 2> /dev/null perl -i -pe 's/Transactions/Brazoles/g;' $file 2> /dev/null perl -i -pe 's/entry/xact/g;' $file 2> /dev/null perl -i -pe 's/ENTRY/XACT/g;' $file 2> /dev/null perl -i -pe 's/Entry/Xact/g;' $file 2> /dev/null perl -i -pe 's/entries/xacts/g;' $file 2> /dev/null perl -i -pe 's/ENTRIES/XACTS/g;' $file 2> /dev/null perl -i -pe 's/Entries/Xacts/g;' $file 2> /dev/null perl -i -pe 's/entrys/xacts/g;' $file 2> /dev/null perl -i -pe 's/ENTRYS/XACTS/g;' $file 2> /dev/null perl -i -pe 's/Entrys/Xacts/g;' $file 2> /dev/null perl -i -pe 's/fazoles/posts/g;' $file 2> /dev/null perl -i -pe 's/FAZOLES/POSTS/g;' $file 2> /dev/null perl -i -pe 's/Fazoles/Posts/g;' $file 2> /dev/null perl -i -pe 's/fazole/post/g;' $file 2> /dev/null perl -i -pe 's/FAZOLE/POST/g;' $file 2> /dev/null perl -i -pe 's/Fazole/Post/g;' $file 2> /dev/null perl -i -pe 's/brazoles/postings/g;' $file 2> /dev/null perl -i -pe 's/BRAZOLES/POSTINGS/g;' $file 2> /dev/null perl -i -pe 's/Brazoles/Postings/g;' $file 2> /dev/null perl -i -pe 's/brazole/posting/g;' $file 2> /dev/null perl -i -pe 's/BRAZOLE/POSTING/g;' $file 2> /dev/null perl -i -pe 's/Brazole/Posting/g;' $file 2> /dev/null perl -i -pe 's/\@dirxact/\@direntry/g;' $file 2> /dev/null perl -i -pe 's/\@end dirxact/\@end direntry/g;' $file 2> /dev/null done mv src/xact.h src/fazole.h mv src/xact.cc src/fazole.cc mv src/entry.h src/xact.h mv src/entry.cc src/xact.cc mv src/fazole.h src/post.h mv src/fazole.cc src/post.cc mv src/py_xact.py src/fazole.py mv src/py_entry.py src/py_xact.py mv src/fazole.py src/py_post.py ln -sf ~/Products/ledger/ledger . tools/myacprep ��������ledger-3.3.2/tools/speed-test.sh��������������������������������������������������������������������0000775�0000000�0000000�00000000531�14411236400�0016527�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash /bin/rm -fr ~/Products/ledger/opt ./acprep -j16 opt update || exit 0 COMMIT=$(git describe --long --all) SPEEDS=$(./acprep -j16 opt make speedtest 2>&1 \ | grep "Finished executing command" \ | awk '{print $1}' \ | xargs) echo $COMMIT,$(echo $SPEEDS | sed 's/ /,/g') >> ~/src/ledger/speed.log exit 0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/speedcmp�������������������������������������������������������������������������0000775�0000000�0000000�00000000616�14411236400�0015645�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # ex: speedcmp ledger ledger-2.4 50 export LEDGER_FILE=$PWD/tools/temp.dat export LEDGER_CACHE=$PWD/tools/temp.cache count=$1 shift 1 for i in "$@"; do echo -n "textual $i: " average -n $count $i -o /dev/null -f $LEDGER reg rent rm -f $LEDGER_CACHE; $i -o /dev/null reg rent > /dev/null 2>&1 echo -n "binary $i: " average -n $count $i -o /dev/null reg rent done ������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/spellcheck.sh��������������������������������������������������������������������0000775�0000000�0000000�00000000134�14411236400�0016566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh aspell --lang=en_US.UTF-8 check --mode=texinfo $(dirname $0)/../doc/ledger3.texi ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/times.sh�������������������������������������������������������������������������0000775�0000000�0000000�00000000363�14411236400�0015576�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh time test/RegressTests.py ./ledger test/regress time test/RegressTests.py ./ledger test/regress --verify #time test/RegressTests.py ./ledger test/regress --gmalloc #time test/RegressTests.py ./ledger test/regress --verify --gmalloc �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ledger-3.3.2/tools/update_copyright_year.sh���������������������������������������������������������0000775�0000000�0000000�00000003346�14411236400�0021053�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # update_copyright_year - Update the year of the Copyright statement in files # # This script will replace the last year of Copyright statements with the first # argument of this script (defaulting to the current year). # Copyright (c) 2016, 2023 Alexis Hildebrandt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. YEAR=${1:-$(date +%Y)} # Use ag (the-silver-searcher) when available as it is much faster than # the venerable egrep GREP=$(command -v ag || command -v egrep) git ls-files -z \ | xargs -0 ${GREP} -Rl 'Copyright.*Wiegley' \ | uniq \ | ${GREP} -v "(test/regress/25A099C9.dat|$(basename $0))" \ | xargs sed -i '' -e "s/\(Copyright.*\)-20[0-9]\{2\}/\1-${YEAR}/" \ # git ls-files | xargs grep | uniq | grep -v | xargs sed ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������