mirror of
https://github.com/PDB-REDO/libcifpp.git
synced 2026-06-07 07:44:22 +08:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9dc5d11829 | ||
|
|
8565e1b408 | ||
|
|
bfc7133786 | ||
|
|
15a49f1bb4 | ||
|
|
db1dff16fe | ||
|
|
8d7d9d3a31 | ||
|
|
078bf8a559 | ||
|
|
1f314a5e9b | ||
|
|
0adb50ac01 | ||
|
|
d91707cd06 | ||
|
|
c0e7ee4eeb | ||
|
|
c143a7223e | ||
|
|
2c951ba146 | ||
|
|
660aadcd9c | ||
|
|
91d6adb980 | ||
|
|
b79ddd55c5 | ||
|
|
0ca645c634 | ||
|
|
676c0c8dc8 | ||
|
|
5c366ad9b1 | ||
|
|
836aed6ea9 | ||
|
|
50df250415 | ||
|
|
2409fc5b7b | ||
|
|
8a1184a24c | ||
|
|
d2fbc54765 | ||
|
|
1bcb26ba75 | ||
|
|
32f4749d84 | ||
|
|
da12be879a | ||
|
|
94a38ad4e8 | ||
|
|
20ef79a172 | ||
|
|
92bf25476e | ||
|
|
b55e074dd7 | ||
|
|
7b654a837d | ||
|
|
ae9d247d22 | ||
|
|
16b7deafe8 | ||
|
|
f2cfe28458 | ||
|
|
2e8a52949e | ||
|
|
441e142767 |
@@ -25,7 +25,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# set the project name
|
||||
project(cifpp VERSION 5.0.9 LANGUAGES CXX)
|
||||
project(libcifpp VERSION 5.1.2 LANGUAGES CXX)
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
@@ -98,33 +98,33 @@ if(BUILD_FOR_CCP4)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(${CMAKE_SYSTEM_VERSION} GREATER_EQUAL 10) # Windows 10
|
||||
add_definitions(-D _WIN32_WINNT=0x0A00)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.3) # Windows 8.1
|
||||
add_definitions(-D _WIN32_WINNT=0x0603)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.2) # Windows 8
|
||||
add_definitions(-D _WIN32_WINNT=0x0602)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.1) # Windows 7
|
||||
add_definitions(-D _WIN32_WINNT=0x0601)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.0) # Windows Vista
|
||||
add_definitions(-D _WIN32_WINNT=0x0600)
|
||||
else() # Windows XP (5.1)
|
||||
add_definitions(-D _WIN32_WINNT=0x0501)
|
||||
endif()
|
||||
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# make msvc standards compliant...
|
||||
add_compile_options(/permissive-)
|
||||
# make msvc standards compliant...
|
||||
add_compile_options(/permissive-)
|
||||
|
||||
macro(get_WIN32_WINNT version)
|
||||
if(CMAKE_SYSTEM_VERSION)
|
||||
set(ver ${CMAKE_SYSTEM_VERSION})
|
||||
string(REGEX MATCH "^([0-9]+).([0-9])" ver ${ver})
|
||||
string(REGEX MATCH "^([0-9]+)" verMajor ${ver})
|
||||
|
||||
# Check for Windows 10, b/c we'll need to convert to hex 'A'.
|
||||
if("${verMajor}" MATCHES "10")
|
||||
set(verMajor "A")
|
||||
string(REGEX REPLACE "^([0-9]+)" ${verMajor} ver ${ver})
|
||||
endif()
|
||||
|
||||
# Remove all remaining '.' characters.
|
||||
string(REPLACE "." "" ver ${ver})
|
||||
|
||||
# Prepend each digit with a zero.
|
||||
string(REGEX REPLACE "([0-9A-Z])" "0\\1" ver ${ver})
|
||||
set(${version} "0x${ver}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
get_WIN32_WINNT(ver)
|
||||
add_definitions(-D_WIN32_WINNT=${ver})
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
else()
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Libraries
|
||||
@@ -197,7 +197,7 @@ endif()
|
||||
|
||||
# Create a revision file, containing the current git version info
|
||||
include(VersionString)
|
||||
write_version_header(${PROJECT_SOURCE_DIR}/src/ "LibCIFPP")
|
||||
write_version_header(${PROJECT_SOURCE_DIR}/src/ LIB_NAME "LibCIFPP")
|
||||
|
||||
# SymOp data table
|
||||
if(CIFPP_RECREATE_SYMOP_DATA)
|
||||
@@ -292,7 +292,7 @@ target_include_directories(cifpp
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
|
||||
target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB ${CIFPP_REQUIRED_LIBRARIES})
|
||||
target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB Eigen3::Eigen ${CIFPP_REQUIRED_LIBRARIES})
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
|
||||
@@ -307,18 +307,7 @@ if(CIFPP_DOWNLOAD_CCD)
|
||||
file(MAKE_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
|
||||
endif()
|
||||
|
||||
find_program(GUNZIP gunzip)
|
||||
|
||||
if(GUNZIP)
|
||||
file(DOWNLOAD ftp://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif.gz ${COMPONENTS_CIF}.gz
|
||||
SHOW_PROGRESS)
|
||||
add_custom_command(OUTPUT ${COMPONENTS_CIF}
|
||||
COMMAND ${GUNZIP} ${COMPONENTS_CIF}.gz
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
|
||||
else()
|
||||
file(DOWNLOAD ftp://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif ${COMPONENTS_CIF}
|
||||
SHOW_PROGRESS)
|
||||
endif()
|
||||
file(DOWNLOAD https://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif ${COMPONENTS_CIF} SHOW_PROGRESS)
|
||||
endif()
|
||||
|
||||
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
|
||||
@@ -382,6 +371,16 @@ install(FILES
|
||||
DESTINATION ${CIFPP_DATA_DIR}
|
||||
)
|
||||
|
||||
if(${CIFPP_CACHE_DIR})
|
||||
install(FILES
|
||||
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
|
||||
${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
|
||||
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ma.dic
|
||||
${COMPONENTS_CIF}
|
||||
DESTINATION ${CIFPP_CACHE_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
set(CONFIG_TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/cmake/cifppConfig.cmake.in)
|
||||
|
||||
configure_package_config_file(
|
||||
@@ -459,7 +458,7 @@ endif()
|
||||
|
||||
# Optionally install the update scripts for CCD and dictionary files
|
||||
if(CIFPP_INSTALL_UPDATE_SCRIPT)
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "GNU")
|
||||
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/cron.weekly")
|
||||
elseif(UNIX) # assume all others are like FreeBSD...
|
||||
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/periodic/weekly")
|
||||
|
||||
73
README.md
73
README.md
@@ -3,18 +3,85 @@ libcifpp
|
||||
|
||||
This library contains code to work with mmCIF and PDB files.
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
```c++
|
||||
// A simple program counting residues with an OXT atom
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
#include <cif++.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2)
|
||||
exit(1);
|
||||
|
||||
// Read file, can be PDB or mmCIF and can even be compressed with gzip.
|
||||
cif::file file = cif::pdb::read(argv[1]);
|
||||
|
||||
if (file.empty())
|
||||
{
|
||||
std::cerr << "Empty file" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Take the first datablock in the file
|
||||
auto &db = file.front();
|
||||
|
||||
// Use the atom_site category
|
||||
auto &atom_site = db["atom_site"];
|
||||
|
||||
// Count the atoms with atom-id "OXT"
|
||||
auto n = atom_site.count(cif::key("label_atom_id") == "OXT");
|
||||
|
||||
std::cout << "File contains " << atom_site.size() << " atoms of which "
|
||||
<< n << (n == 1 ? " is" : " are") << " OXT" << std::endl
|
||||
<< "residues with an OXT are:" << std::endl;
|
||||
|
||||
// Loop over all atoms with atom-id "OXT" and print out some info.
|
||||
// That info is extracted using structured binding in C++
|
||||
for (const auto &[asym, comp, seqnr] :
|
||||
atom_site.find<std::string, std::string, int>(
|
||||
cif::key("label_atom_id") == "OXT",
|
||||
"label_asym_id", "label_comp_id", "label_seq_id"))
|
||||
{
|
||||
std::cout << asym << ' ' << comp << ' ' << seqnr << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
The code for this library was written in C++17. You therefore need a
|
||||
recent compiler to build it. For the development gcc 9.3 and clang 9.0
|
||||
recent compiler to build it. For the development gcc 9.4 and clang 9.0
|
||||
have been used as well as MSVC version 2019.
|
||||
|
||||
Other requirements are:
|
||||
|
||||
- [mrc](https://github.com/mhekkel/mrc), a resource compiler that
|
||||
allows including data files into the executable making them easier to
|
||||
install. Strictly this is optional, but at the expense of functionality.
|
||||
install. Strictly speaking this is optional, but at the expense of
|
||||
functionality.
|
||||
- [libeigen](https://eigen.tuxfamily.org/index.php?title=Main_Page), a
|
||||
library to do amongst others matrix calculations. This usually can be
|
||||
installed using your package manager, in Debian/Ubuntu it is called
|
||||
`libeigen3-dev`
|
||||
- zlib, the development version of this library. On Debian/Ubuntu this
|
||||
is the package `zlib1g-dev`.
|
||||
- [boost](https://www.boost.org). The boost libraries are only needed if
|
||||
you want to build the testing code.
|
||||
|
||||
When building using MS Visual Studio, you will also need [libzeep](https://github.com/mhekkel/libzeep)
|
||||
since MSVC does not yet provide a C++ template required by libcifpp.
|
||||
|
||||
Building
|
||||
--------
|
||||
@@ -26,7 +93,7 @@ On linux e.g. you would issue the following commands to build and install
|
||||
libcifpp in your `$HOME/.local` folder:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/PDB-REDO/libcifpp.git
|
||||
git clone https://github.com/PDB-REDO/libcifpp.git --recurse-submodules
|
||||
cd libcifpp
|
||||
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build
|
||||
|
||||
18
changelog
18
changelog
@@ -1,3 +1,21 @@
|
||||
Version 5.1.2
|
||||
- New version string code
|
||||
- Added check for Eigen3 in cifppConfig.cmake
|
||||
|
||||
Version 5.1.1
|
||||
- Added missing include <compare> in symmetry.hpp
|
||||
- Added empty() to matrix
|
||||
- Fix for parsing PDB files with a last line that does not end with
|
||||
a new line character.
|
||||
|
||||
Version 5.1
|
||||
- New parser, optimised for speed
|
||||
- Fix in unique ID generator
|
||||
|
||||
Version 5.0.10
|
||||
- Fix in progress_bar, was using too much CPU
|
||||
- Optimised mmCIF parser
|
||||
|
||||
Version 5.0.9
|
||||
- Fix in dihedral angle calculations
|
||||
- Added create_water to model
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
# - Returns a version string from Git
|
||||
#
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar> [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR])
|
||||
#
|
||||
# Returns the refspec and sha hash of the current head revision
|
||||
#
|
||||
# git_describe(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe on the source tree, and adjusting
|
||||
# the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_describe_working_tree(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe on the working tree (--dirty option),
|
||||
# and adjusting the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe --exact-match on the source tree,
|
||||
# and adjusting the output so that it tests false if there was no exact
|
||||
# matching tag.
|
||||
#
|
||||
# git_local_changes(<var>)
|
||||
#
|
||||
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
|
||||
# Uses the return code of "git diff-index --quiet HEAD --".
|
||||
# Does not regard untracked files.
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
#
|
||||
# Copyright 2009-2013, Iowa State University.
|
||||
# Copyright 2013-2020, Ryan Pavlik
|
||||
# Copyright 2013-2020, Contributors
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__get_git_revision_description)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_revision_description YES)
|
||||
|
||||
# We must run the following at "include" time, not at function call time,
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
# Function _git_find_closest_git_dir finds the next closest .git directory
|
||||
# that is part of any directory in the path defined by _start_dir.
|
||||
# The result is returned in the parent scope variable whose name is passed
|
||||
# as variable _git_dir_var. If no .git directory can be found, the
|
||||
# function returns an empty string via _git_dir_var.
|
||||
#
|
||||
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
||||
# neither foo nor bar contain a file/directory .git. This wil return
|
||||
# C:/bla/.git
|
||||
#
|
||||
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
||||
set(cur_dir "${_start_dir}")
|
||||
set(git_dir "${_start_dir}/.git")
|
||||
while(NOT EXISTS "${git_dir}")
|
||||
# .git dir not found, search parent directories
|
||||
set(git_previous_parent "${cur_dir}")
|
||||
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
|
||||
if(cur_dir STREQUAL git_previous_parent)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_git_dir_var}
|
||||
""
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(git_dir "${cur_dir}/.git")
|
||||
endwhile()
|
||||
set(${_git_dir_var}
|
||||
"${git_dir}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
|
||||
|
||||
if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
|
||||
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE)
|
||||
else()
|
||||
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE)
|
||||
endif()
|
||||
if(NOT "${GIT_DIR}" STREQUAL "")
|
||||
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
|
||||
"${GIT_DIR}")
|
||||
if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
|
||||
# We've gone above the CMake root dir.
|
||||
set(GIT_DIR "")
|
||||
endif()
|
||||
endif()
|
||||
if("${GIT_DIR}" STREQUAL "")
|
||||
set(${_refspecvar}
|
||||
"GITDIR-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
set(${_hashvar}
|
||||
"GITDIR-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check if the current source dir is a git submodule or a worktree.
|
||||
# In both cases .git is a file instead of a directory.
|
||||
#
|
||||
if(NOT IS_DIRECTORY ${GIT_DIR})
|
||||
# The following git command will return a non empty string that
|
||||
# points to the super project working tree if the current
|
||||
# source dir is inside a git submodule.
|
||||
# Otherwise the command will return an empty string.
|
||||
#
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse
|
||||
--show-superproject-working-tree
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT "${out}" STREQUAL "")
|
||||
# If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
|
||||
${submodule})
|
||||
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
|
||||
ABSOLUTE)
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
else()
|
||||
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
|
||||
file(READ ${GIT_DIR} worktree_ref)
|
||||
# The .git directory contains a path to the worktree information directory
|
||||
# inside the parent git repo of the worktree.
|
||||
#
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
|
||||
${worktree_ref})
|
||||
string(STRIP ${git_worktree_dir} git_worktree_dir)
|
||||
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
|
||||
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
|
||||
endif()
|
||||
else()
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
endif()
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if(NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${HEAD_SOURCE_FILE}")
|
||||
return()
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
|
||||
"${GIT_DATA}/grabRef.cmake" @ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar}
|
||||
"${HEAD_REF}"
|
||||
PARENT_SCOPE)
|
||||
set(${_hashvar}
|
||||
"${HEAD_HASH}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var}
|
||||
"HEAD-HASH-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# TODO sanitize
|
||||
#if((${ARGN}" MATCHES "&&") OR
|
||||
# (ARGN MATCHES "||") OR
|
||||
# (ARGN MATCHES "\\;"))
|
||||
# message("Please report the following error to the project!")
|
||||
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
|
||||
#endif()
|
||||
|
||||
#message(STATUS "Arguments to execute_process: ${ARGN}")
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe_working_tree _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_get_exact_tag _var)
|
||||
git_describe(out --exact-match ${ARGN})
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_local_changes _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var}
|
||||
"HEAD-HASH-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(res EQUAL 0)
|
||||
set(${_var}
|
||||
"CLEAN"
|
||||
PARENT_SCOPE)
|
||||
else()
|
||||
set(${_var}
|
||||
"DIRTY"
|
||||
PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,43 +0,0 @@
|
||||
#
|
||||
# Internal file for GetGitRevisionDescription.cmake
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright 2009-2012, Iowa State University
|
||||
# Copyright 2011-2015, Contributors
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if(HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
else()
|
||||
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
|
||||
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
||||
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
# Copyright (c) 2021 NKI/AVL, Netherlands Cancer Institute
|
||||
# Copyright (c) 2021-2023 Maarten L. Hekkelman
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,60 +22,374 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# This cmake extension writes out a revision.hpp file in a specified directory.
|
||||
# The file will contain a C++ inline function that can be used to write out
|
||||
# version information.
|
||||
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
# We want the revision.hpp file to be updated whenever the status of the
|
||||
# git repository changes. Use the same technique as in GetGitRevisionDescription.cmake
|
||||
# from https://github.com/rpavlik/cmake-modules
|
||||
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
.. command:: write_version_header
|
||||
|
||||
Write a file named revision.hpp containing version info::
|
||||
|
||||
write_version_header(<destdir>
|
||||
[FILE_NAME <file-name>]
|
||||
[LIB_NAME <library-name>]
|
||||
)
|
||||
|
||||
This command will generate the code to write a file name
|
||||
revision.hpp in the directory ``<destdir>``.
|
||||
|
||||
``FILE_NAME``
|
||||
Specify the name of the file to create, default is ``revision.hpp``.
|
||||
|
||||
``LIB_NAME``
|
||||
Specify the library name which will be used as a prefix part for the
|
||||
variables contained in the revision file.
|
||||
#]=======================================================================]
|
||||
|
||||
# First locate a .git file or directory.
|
||||
function(_get_git_dir _start_dir _variable)
|
||||
|
||||
set(cur_dir "${_start_dir}")
|
||||
set(git_dir "${_start_dir}/.git")
|
||||
|
||||
while(NOT EXISTS "${git_dir}")
|
||||
# .git dir not found, search parent directories
|
||||
set(prev_dir "${cur_dir}")
|
||||
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
|
||||
if(cur_dir STREQUAL prev_dir OR cur_dir STREQUAL ${_start_dir})
|
||||
# we are not in git since we either hit root or
|
||||
# the ${_start_dir} which should be the top
|
||||
set(${_variable} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(git_dir "${cur_dir}/.git")
|
||||
endwhile()
|
||||
|
||||
set(${_variable} "${git_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Locate the git refspec hash and load the hash
|
||||
# This code locates the file containing the git refspec/hash
|
||||
# and loads it. Doing it this way assures that each time the git
|
||||
# repository changes the revision.hpp file gets out of date.
|
||||
function(_get_git_hash _data_dir _variable)
|
||||
|
||||
# Be pessimistic
|
||||
set(_variable "" PARENT_SCOPE)
|
||||
|
||||
# Load git package if needed
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
|
||||
# And fail if not found
|
||||
if(NOT GIT_FOUND)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Locate the nearest .git file or directory
|
||||
_get_git_dir(${CMAKE_CURRENT_SOURCE_DIR} GIT_DIR)
|
||||
|
||||
# And fail if not found
|
||||
if("${GIT_DIR}" STREQUAL "")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check if the current source dir is a git submodule or a worktree.
|
||||
# In both cases .git is a file instead of a directory.
|
||||
#
|
||||
if(IS_DIRECTORY ${GIT_DIR})
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
else()
|
||||
# The following git command will return a non empty string that
|
||||
# points to the super project working tree if the current
|
||||
# source dir is inside a git submodule.
|
||||
# Otherwise the command will return an empty string.
|
||||
#
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse
|
||||
--show-superproject-working-tree
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT "${out}" STREQUAL "")
|
||||
# If out is not empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
|
||||
${submodule})
|
||||
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
|
||||
ABSOLUTE)
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
else()
|
||||
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
|
||||
file(READ ${GIT_DIR} worktree_ref)
|
||||
# The .git directory contains a path to the worktree information directory
|
||||
# inside the parent git repo of the worktree.
|
||||
#
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
|
||||
${worktree_ref})
|
||||
string(STRIP ${git_worktree_dir} git_worktree_dir)
|
||||
_get_git_dir("${git_worktree_dir}" GIT_DIR)
|
||||
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Fail if the 'head' file was not found
|
||||
if(NOT EXISTS "${HEAD_SOURCE_FILE}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Make a copy of the head file
|
||||
set(HEAD_FILE "${_data_dir}/HEAD")
|
||||
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
# Now we create a cmake file that will read the contents of this
|
||||
# head file in the appropriate way
|
||||
file(WRITE "${_data_dir}/grab-ref.cmake.in" [[
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if(HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@VERSION_STRING_DATA@/head-ref" COPYONLY)
|
||||
else()
|
||||
configure_file("@GIT_DIR@/packed-refs" "@VERSION_STRING_DATA@/packed-refs" COPYONLY)
|
||||
file(READ "@VERSION_STRING_DATA@/packed-refs" PACKED_REFS)
|
||||
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@VERSION_STRING_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
file(READ "@VERSION_STRING_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
||||
]])
|
||||
|
||||
configure_file("${VERSION_STRING_DATA}/grab-ref.cmake.in"
|
||||
"${VERSION_STRING_DATA}/grab-ref.cmake" @ONLY)
|
||||
|
||||
# Include the aforementioned file, this will define
|
||||
# the HEAD_HASH variable we're looking for
|
||||
include("${VERSION_STRING_DATA}/grab-ref.cmake")
|
||||
|
||||
set(${_variable} "${HEAD_HASH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Create a revision file, containing the current git version info, if any
|
||||
function(write_version_header dir)
|
||||
|
||||
set(flags )
|
||||
set(options LIB_NAME FILE_NAME)
|
||||
set(sources )
|
||||
cmake_parse_arguments(VERSION_STRING_OPTION "${flags}" "${options}" "${sources}" ${ARGN})
|
||||
|
||||
# parameter check
|
||||
if(NOT IS_DIRECTORY ${dir})
|
||||
message(FATAL_ERROR "First parameter to write_version_header should be a directory where the final revision.hpp file will be placed")
|
||||
endif()
|
||||
|
||||
include(GetGitRevisionDescription)
|
||||
if(NOT(GIT-NOTFOUND OR HEAD-HASH-NOTFOUND))
|
||||
git_describe_working_tree(BUILD_VERSION_STRING --match=build --dirty)
|
||||
|
||||
if(BUILD_VERSION_STRING MATCHES "build-([0-9]+)-g([0-9a-f]+)(-dirty)?")
|
||||
set(BUILD_GIT_TAGREF "${CMAKE_MATCH_2}")
|
||||
if(CMAKE_MATCH_3)
|
||||
set(BUILD_VERSION_STRING "${CMAKE_MATCH_1}*")
|
||||
else()
|
||||
set(BUILD_VERSION_STRING "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
if(VERSION_STRING_OPTION_FILE_NAME)
|
||||
set(file_name "${VERSION_STRING_OPTION_FILE_NAME}")
|
||||
else()
|
||||
message(WARNING "no git info available, cannot update version string")
|
||||
set(file_name "revision.hpp")
|
||||
endif()
|
||||
|
||||
string(TIMESTAMP BUILD_DATE_TIME "%Y-%m-%dT%H:%M:%SZ" UTC)
|
||||
# Where to store intermediate files
|
||||
set(VERSION_STRING_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/VersionString")
|
||||
if(NOT EXISTS "${VERSION_STRING_DATA}")
|
||||
file(MAKE_DIRECTORY "${VERSION_STRING_DATA}")
|
||||
endif()
|
||||
|
||||
if(ARGC GREATER 1)
|
||||
set(VAR_PREFIX "${ARGV1}")
|
||||
# Load the git hash using the wizzard-like code above.
|
||||
_get_git_hash("${VERSION_STRING_DATA}" GIT_HASH)
|
||||
|
||||
# If git was found, fetch the git description string
|
||||
if(GIT_HASH)
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --dirty --match=build
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
if(res EQUAL 0)
|
||||
set(REVISION_STRING "${out}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(WRITE "${PROJECT_BINARY_DIR}/revision.hpp.in" [[// Generated revision file
|
||||
# Check the revision string, if it matches we fill in the required info
|
||||
if(REVISION_STRING MATCHES "build-([0-9]+)-g([0-9a-f]+)(-dirty)?")
|
||||
set(BUILD_NUMBER ${CMAKE_MATCH_1})
|
||||
if(CMAKE_MATCH_3)
|
||||
set(REVISION_GIT_TAGREF "${CMAKE_MATCH_2}*")
|
||||
else()
|
||||
set(REVISION_GIT_TAGREF "${CMAKE_MATCH_2}")
|
||||
endif()
|
||||
|
||||
string(TIMESTAMP REVISION_DATE_TIME "%Y-%m-%dT%H:%M:%SZ" UTC)
|
||||
else()
|
||||
set(REVISION_GIT_TAGREF "")
|
||||
set(BUILD_NUMBER 0)
|
||||
set(REVISION_DATE_TIME "")
|
||||
endif()
|
||||
|
||||
if(VERSION_STRING_OPTION_LIB_NAME)
|
||||
set(VAR_PREFIX "${VERSION_STRING_OPTION_LIB_NAME}")
|
||||
set(IDENT_PREFIX "${VERSION_STRING_OPTION_LIB_NAME}_")
|
||||
else()
|
||||
set(VAR_PREFIX "")
|
||||
set(IDENT_PREFIX "")
|
||||
endif()
|
||||
|
||||
# And finally, write out the header file
|
||||
file(WRITE "${VERSION_STRING_DATA}/${file_name}.in" [[// This file was generated by VersionString.cmake
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
|
||||
const char k@VAR_PREFIX@ProjectName[] = "@PROJECT_NAME@";
|
||||
const char k@VAR_PREFIX@VersionNumber[] = "@PROJECT_VERSION@";
|
||||
const char k@VAR_PREFIX@VersionGitTag[] = "@BUILD_GIT_TAGREF@";
|
||||
const char k@VAR_PREFIX@BuildInfo[] = "@BUILD_VERSION_STRING@";
|
||||
const char k@VAR_PREFIX@BuildDate[] = "@BUILD_DATE_TIME@";
|
||||
constexpr const char k@VAR_PREFIX@ProjectName[] = "@PROJECT_NAME@";
|
||||
constexpr const char k@VAR_PREFIX@VersionNumber[] = "@PROJECT_VERSION@";
|
||||
constexpr int k@VAR_PREFIX@BuildNumber = @BUILD_NUMBER@;
|
||||
constexpr const char k@VAR_PREFIX@RevisionGitTag[] = "@REVISION_GIT_TAGREF@";
|
||||
constexpr const char k@VAR_PREFIX@RevisionDate[] = "@REVISION_DATE_TIME@";
|
||||
|
||||
#ifndef VERSION_INFO_DEFINED
|
||||
#define VERSION_INFO_DEFINED 1
|
||||
|
||||
namespace version_info_v1
|
||||
{
|
||||
|
||||
class version_info_base
|
||||
{
|
||||
public:
|
||||
|
||||
static void write(std::ostream &os, bool verbose)
|
||||
{
|
||||
auto &s_head = head();
|
||||
if (s_head != nullptr)
|
||||
write(s_head, os, verbose);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
version_info_base(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date)
|
||||
: m_name(name)
|
||||
, m_version(version)
|
||||
, m_build_number(build_number)
|
||||
, m_git_tag(git_tag)
|
||||
, m_revision_date(revision_date)
|
||||
{
|
||||
auto &s_head = head();
|
||||
m_next = s_head;
|
||||
s_head = this;
|
||||
}
|
||||
|
||||
static void write(const version_info_base *inst, std::ostream &os, bool verbose)
|
||||
{
|
||||
if (inst->m_next)
|
||||
{
|
||||
write(inst->m_next, os, verbose);
|
||||
|
||||
if (not verbose)
|
||||
return;
|
||||
|
||||
os << '-' << std::endl;
|
||||
}
|
||||
|
||||
os << inst->m_name << " version " << inst->m_version << std::endl;
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
if (inst->m_build_number != 0)
|
||||
{
|
||||
os << "build: " << inst->m_build_number << ' ' << inst->m_revision_date << std::endl;
|
||||
if (inst->m_git_tag[0] != 0)
|
||||
os << "git tag: " << inst->m_git_tag << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using version_info_ptr = version_info_base *;
|
||||
|
||||
static version_info_ptr &head()
|
||||
{
|
||||
static version_info_ptr s_head = nullptr;
|
||||
return s_head;
|
||||
}
|
||||
|
||||
const char *m_name;
|
||||
const char *m_version;
|
||||
int m_build_number;
|
||||
const char *m_git_tag;
|
||||
const char *m_revision_date;
|
||||
version_info_base *m_next = nullptr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class version_info : public version_info_base
|
||||
{
|
||||
public:
|
||||
using implementation_type = T;
|
||||
|
||||
version_info(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date)
|
||||
: version_info_base(name, version, build_number, git_tag, revision_date)
|
||||
{
|
||||
}
|
||||
|
||||
struct register_object
|
||||
{
|
||||
register_object()
|
||||
{
|
||||
static implementation_type s_instance;
|
||||
}
|
||||
};
|
||||
|
||||
template<register_object&> struct reference_object;
|
||||
|
||||
static register_object s_registered_object;
|
||||
static reference_object<s_registered_object> s_referenced_object;
|
||||
};
|
||||
|
||||
template<typename T> typename version_info<T>::register_object version_info<T>::s_registered_object;
|
||||
|
||||
}
|
||||
|
||||
inline void write_version_string(std::ostream &os, bool verbose)
|
||||
{
|
||||
os << k@VAR_PREFIX@ProjectName << " version " << k@VAR_PREFIX@VersionNumber << std::endl;
|
||||
if (verbose)
|
||||
{
|
||||
os << "build: " << k@VAR_PREFIX@BuildInfo << ' ' << k@VAR_PREFIX@BuildDate << std::endl;
|
||||
if (k@VAR_PREFIX@VersionGitTag[0] != 0)
|
||||
os << "git tag: " << k@VAR_PREFIX@VersionGitTag << std::endl;
|
||||
}
|
||||
version_info_v1::version_info_base::write(os, verbose);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class version_info_@IDENT_PREFIX@impl : public version_info_v1::version_info<version_info_@IDENT_PREFIX@impl>
|
||||
{
|
||||
public:
|
||||
version_info_@IDENT_PREFIX@impl()
|
||||
: version_info(k@VAR_PREFIX@ProjectName, k@VAR_PREFIX@VersionNumber, k@VAR_PREFIX@BuildNumber, k@VAR_PREFIX@RevisionGitTag, k@VAR_PREFIX@RevisionDate)
|
||||
{
|
||||
}
|
||||
};
|
||||
]])
|
||||
configure_file("${PROJECT_BINARY_DIR}/revision.hpp.in" "${dir}/revision.hpp" @ONLY)
|
||||
configure_file("${VERSION_STRING_DATA}/${file_name}.in" "${dir}/${file_name}" @ONLY)
|
||||
endfunction()
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cifppTargets.cmake")
|
||||
|
||||
# Note that this set_and_check needs te be executed before
|
||||
# find_dependency of Eigen3, otherwise the path is
|
||||
# not found....
|
||||
set_and_check(CIFPP_SHARE_DIR "@PACKAGE_CIFPP_DATA_DIR@")
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(Threads)
|
||||
|
||||
find_dependency(ZLIB REQUIRED)
|
||||
find_dependency(Eigen3 REQUIRED)
|
||||
|
||||
if(MSVC)
|
||||
find_dependency(zeep REQUIRED)
|
||||
endif()
|
||||
|
||||
INCLUDE("${CMAKE_CURRENT_LIST_DIR}/cifppTargets.cmake")
|
||||
|
||||
set_and_check(CIFPP_SHARE_DIR "@PACKAGE_CIFPP_DATA_DIR@")
|
||||
|
||||
check_required_components(cifpp)
|
||||
|
||||
@@ -1,24 +1,32 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
#include <cif++.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
int main()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
cif::file file;
|
||||
file.load("1cbs.cif.gz");
|
||||
if (argc != 2)
|
||||
exit(1);
|
||||
|
||||
auto& db = file.front();
|
||||
cif::file file = cif::pdb::read(argv[1]);
|
||||
|
||||
if (file.empty())
|
||||
{
|
||||
std::cerr << "Empty file" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto &db = file.front();
|
||||
auto &atom_site = db["atom_site"];
|
||||
auto n = atom_site.find(cif::key("label_atom_id") == "OXT").size();
|
||||
|
||||
std::cout << "File contains " << atom_site.size() << " atoms of which " << n << (n == 1 ? " is" : " are") << " OXT" << std::endl
|
||||
<< "residues with an OXT are:" << std::endl;
|
||||
|
||||
for (const auto& [asym, comp, seqnr]: atom_site.find<std::string,std::string,int>(
|
||||
cif::key("label_atom_id") == "OXT", "label_asym_id", "label_comp_id", "label_seq_id"))
|
||||
<< "residues with an OXT are:" << std::endl;
|
||||
|
||||
for (const auto &[asym, comp, seqnr] : atom_site.find<std::string, std::string, int>(
|
||||
cif::key("label_atom_id") == "OXT", "label_asym_id", "label_comp_id", "label_seq_id"))
|
||||
{
|
||||
std::cout << asym << ' ' << comp << ' ' << seqnr << std::endl;
|
||||
}
|
||||
|
||||
@@ -32,5 +32,6 @@ namespace cif
|
||||
{
|
||||
|
||||
validator parse_dictionary(std::string_view name, std::istream &is);
|
||||
void extend_dictionary(validator &v, std::istream &is);
|
||||
|
||||
} // namespace cif
|
||||
|
||||
@@ -246,10 +246,13 @@ class basic_igzip_streambuf : public basic_streambuf<CharT, Traits>
|
||||
zstream.avail_in = static_cast<uInt>(this->m_upstream->sgetn(m_in_buffer.data(), m_in_buffer.size()));
|
||||
}
|
||||
|
||||
if (zstream.avail_in == 0)
|
||||
break;
|
||||
|
||||
int err = ::inflate(&zstream, Z_SYNC_FLUSH);
|
||||
std::streamsize n = kBufferByteSize - zstream.avail_out;
|
||||
|
||||
if (err == Z_STREAM_END or (err == Z_OK and n > 0))
|
||||
if (n > 0)
|
||||
{
|
||||
this->setg(
|
||||
m_out_buffer.data(),
|
||||
@@ -258,6 +261,9 @@ class basic_igzip_streambuf : public basic_streambuf<CharT, Traits>
|
||||
break;
|
||||
}
|
||||
|
||||
if (err == Z_STREAM_END and zstream.avail_in > 0)
|
||||
err = ::inflateReset2(&zstream, 47);
|
||||
|
||||
if (err < Z_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -44,22 +44,24 @@ template <typename M>
|
||||
class matrix_expression
|
||||
{
|
||||
public:
|
||||
constexpr uint32_t dim_m() const { return static_cast<const M &>(*this).dim_m(); }
|
||||
constexpr uint32_t dim_n() const { return static_cast<const M &>(*this).dim_n(); }
|
||||
constexpr size_t dim_m() const { return static_cast<const M &>(*this).dim_m(); }
|
||||
constexpr size_t dim_n() const { return static_cast<const M &>(*this).dim_n(); }
|
||||
|
||||
constexpr auto &operator()(uint32_t i, uint32_t j)
|
||||
constexpr bool empty() const { return dim_m() == 0 or dim_n() == 0; }
|
||||
|
||||
constexpr auto &operator()(size_t i, size_t j)
|
||||
{
|
||||
return static_cast<M &>(*this).operator()(i, j);
|
||||
}
|
||||
|
||||
constexpr auto operator()(uint32_t i, uint32_t j) const
|
||||
constexpr auto operator()(size_t i, size_t j) const
|
||||
{
|
||||
return static_cast<const M &>(*this).operator()(i, j);
|
||||
}
|
||||
|
||||
void swap_row(uint32_t r1, uint32_t r2)
|
||||
void swap_row(size_t r1, size_t r2)
|
||||
{
|
||||
for (uint32_t c = 0; c < dim_m(); ++c)
|
||||
for (size_t c = 0; c < dim_m(); ++c)
|
||||
{
|
||||
auto v = operator()(r1, c);
|
||||
operator()(r1, c) = operator()(r2, c);
|
||||
@@ -67,9 +69,9 @@ class matrix_expression
|
||||
}
|
||||
}
|
||||
|
||||
void swap_col(uint32_t c1, uint32_t c2)
|
||||
void swap_col(size_t c1, size_t c2)
|
||||
{
|
||||
for (uint32_t r = 0; r < dim_n(); ++r)
|
||||
for (size_t r = 0; r < dim_n(); ++r)
|
||||
{
|
||||
auto &a = operator()(r, c1);
|
||||
auto &b = operator()(r, c2);
|
||||
@@ -120,9 +122,9 @@ class matrix : public matrix_expression<matrix<F>>
|
||||
, m_n(m.dim_n())
|
||||
, m_data(m_m * m_n)
|
||||
{
|
||||
for (uint32_t i = 0; i < m_m; ++i)
|
||||
for (size_t i = 0; i < m_m; ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < m_n; ++j)
|
||||
for (size_t j = 0; j < m_n; ++j)
|
||||
operator()(i, j) = m(i, j);
|
||||
}
|
||||
}
|
||||
@@ -178,9 +180,9 @@ class matrix_fixed : public matrix_expression<matrix_fixed<F, M, N>>
|
||||
matrix_fixed(const M2 &m)
|
||||
{
|
||||
assert(M == m.dim_m() and N == m.dim_n());
|
||||
for (uint32_t i = 0; i < M; ++i)
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < N; ++j)
|
||||
for (size_t j = 0; j < N; ++j)
|
||||
operator()(i, j) = m(i, j);
|
||||
}
|
||||
}
|
||||
@@ -242,7 +244,7 @@ class symmetric_matrix : public matrix_expression<symmetric_matrix<F>>
|
||||
public:
|
||||
using value_type = F;
|
||||
|
||||
symmetric_matrix(uint32_t n, value_type v = 0)
|
||||
symmetric_matrix(size_t n, value_type v = 0)
|
||||
: m_n(n)
|
||||
, m_data((m_n * (m_n + 1)) / 2)
|
||||
{
|
||||
@@ -255,17 +257,17 @@ class symmetric_matrix : public matrix_expression<symmetric_matrix<F>>
|
||||
symmetric_matrix &operator=(symmetric_matrix &&m) = default;
|
||||
symmetric_matrix &operator=(const symmetric_matrix &m) = default;
|
||||
|
||||
constexpr uint32_t dim_m() const { return m_n; }
|
||||
constexpr uint32_t dim_n() const { return m_n; }
|
||||
constexpr size_t dim_m() const { return m_n; }
|
||||
constexpr size_t dim_n() const { return m_n; }
|
||||
|
||||
constexpr value_type operator()(uint32_t i, uint32_t j) const
|
||||
constexpr value_type operator()(size_t i, size_t j) const
|
||||
{
|
||||
return i < j
|
||||
? m_data[(j * (j + 1)) / 2 + i]
|
||||
: m_data[(i * (i + 1)) / 2 + j];
|
||||
}
|
||||
|
||||
constexpr value_type &operator()(uint32_t i, uint32_t j)
|
||||
constexpr value_type &operator()(size_t i, size_t j)
|
||||
{
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
@@ -274,7 +276,7 @@ class symmetric_matrix : public matrix_expression<symmetric_matrix<F>>
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_n;
|
||||
size_t m_n;
|
||||
std::vector<value_type> m_data;
|
||||
};
|
||||
|
||||
@@ -296,17 +298,17 @@ class symmetric_matrix_fixed : public matrix_expression<symmetric_matrix_fixed<F
|
||||
symmetric_matrix_fixed &operator=(symmetric_matrix_fixed &&m) = default;
|
||||
symmetric_matrix_fixed &operator=(const symmetric_matrix_fixed &m) = default;
|
||||
|
||||
constexpr uint32_t dim_m() const { return M; }
|
||||
constexpr uint32_t dim_n() const { return M; }
|
||||
constexpr size_t dim_m() const { return M; }
|
||||
constexpr size_t dim_n() const { return M; }
|
||||
|
||||
constexpr value_type operator()(uint32_t i, uint32_t j) const
|
||||
constexpr value_type operator()(size_t i, size_t j) const
|
||||
{
|
||||
return i < j
|
||||
? m_data[(j * (j + 1)) / 2 + i]
|
||||
: m_data[(i * (i + 1)) / 2 + j];
|
||||
}
|
||||
|
||||
constexpr value_type &operator()(uint32_t i, uint32_t j)
|
||||
constexpr value_type &operator()(size_t i, size_t j)
|
||||
{
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
@@ -332,21 +334,21 @@ class identity_matrix : public matrix_expression<identity_matrix<F>>
|
||||
public:
|
||||
using value_type = F;
|
||||
|
||||
identity_matrix(uint32_t n)
|
||||
identity_matrix(size_t n)
|
||||
: m_n(n)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr uint32_t dim_m() const { return m_n; }
|
||||
constexpr uint32_t dim_n() const { return m_n; }
|
||||
constexpr size_t dim_m() const { return m_n; }
|
||||
constexpr size_t dim_n() const { return m_n; }
|
||||
|
||||
constexpr value_type operator()(uint32_t i, uint32_t j) const
|
||||
constexpr value_type operator()(size_t i, size_t j) const
|
||||
{
|
||||
return i == j ? 1 : 0;
|
||||
return static_cast<value_type>(i == j ? 1 : 0);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_n;
|
||||
size_t m_n;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -364,10 +366,10 @@ class matrix_subtraction : public matrix_expression<matrix_subtraction<M1, M2>>
|
||||
assert(m_m1.dim_n() == m_m2.dim_n());
|
||||
}
|
||||
|
||||
constexpr uint32_t dim_m() const { return m_m1.dim_m(); }
|
||||
constexpr uint32_t dim_n() const { return m_m1.dim_n(); }
|
||||
constexpr size_t dim_m() const { return m_m1.dim_m(); }
|
||||
constexpr size_t dim_n() const { return m_m1.dim_n(); }
|
||||
|
||||
constexpr auto operator()(uint32_t i, uint32_t j) const
|
||||
constexpr auto operator()(size_t i, size_t j) const
|
||||
{
|
||||
return m_m1(i, j) - m_m2(i, j);
|
||||
}
|
||||
@@ -394,16 +396,16 @@ class matrix_matrix_multiplication : public matrix_expression<matrix_matrix_mult
|
||||
assert(m1.dim_m() == m2.dim_n());
|
||||
}
|
||||
|
||||
constexpr uint32_t dim_m() const { return m_m1.dim_m(); }
|
||||
constexpr uint32_t dim_n() const { return m_m1.dim_n(); }
|
||||
constexpr size_t dim_m() const { return m_m1.dim_m(); }
|
||||
constexpr size_t dim_n() const { return m_m1.dim_n(); }
|
||||
|
||||
constexpr auto operator()(uint32_t i, uint32_t j) const
|
||||
constexpr auto operator()(size_t i, size_t j) const
|
||||
{
|
||||
using value_type = decltype(m_m1(0, 0));
|
||||
|
||||
value_type result = {};
|
||||
|
||||
for (uint32_t k = 0; k < m_m1.dim_m(); ++k)
|
||||
for (size_t k = 0; k < m_m1.dim_m(); ++k)
|
||||
result += m_m1(i, k) * m_m2(k, j);
|
||||
|
||||
return result;
|
||||
@@ -426,10 +428,10 @@ class matrix_scalar_multiplication : public matrix_expression<matrix_scalar_mult
|
||||
{
|
||||
}
|
||||
|
||||
constexpr uint32_t dim_m() const { return m_m.dim_m(); }
|
||||
constexpr uint32_t dim_n() const { return m_m.dim_n(); }
|
||||
constexpr size_t dim_m() const { return m_m.dim_m(); }
|
||||
constexpr size_t dim_n() const { return m_m.dim_n(); }
|
||||
|
||||
constexpr auto operator()(uint32_t i, uint32_t j) const
|
||||
constexpr auto operator()(size_t i, size_t j) const
|
||||
{
|
||||
return m_m(i, j) * m_v;
|
||||
}
|
||||
@@ -498,10 +500,10 @@ class matrix_cofactors : public matrix_expression<matrix_cofactors<M>>
|
||||
{
|
||||
}
|
||||
|
||||
constexpr uint32_t dim_m() const { return m_m.dim_m(); }
|
||||
constexpr uint32_t dim_n() const { return m_m.dim_n(); }
|
||||
constexpr size_t dim_m() const { return m_m.dim_m(); }
|
||||
constexpr size_t dim_n() const { return m_m.dim_n(); }
|
||||
|
||||
constexpr auto operator()(uint32_t i, uint32_t j) const
|
||||
constexpr auto operator()(size_t i, size_t j) const
|
||||
{
|
||||
const size_t ixs[4][3] = {
|
||||
{ 1, 2, 3 },
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <regex>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
@@ -54,8 +53,6 @@ class sac_parser
|
||||
public:
|
||||
using datablock_index = std::map<std::string, std::size_t>;
|
||||
|
||||
sac_parser(std::istream &is, bool init = true);
|
||||
|
||||
virtual ~sac_parser() = default;
|
||||
|
||||
enum CharTraitsMask : uint8_t
|
||||
@@ -66,9 +63,14 @@ class sac_parser
|
||||
kAnyPrintMask = 1 << 3
|
||||
};
|
||||
|
||||
static bool is_white(int ch)
|
||||
static constexpr bool is_space(int ch)
|
||||
{
|
||||
return std::isspace(ch) or ch == '#';
|
||||
return ch == ' ' or ch == '\t' or ch == '\r' or ch == '\n';
|
||||
}
|
||||
|
||||
static constexpr bool is_white(int ch)
|
||||
{
|
||||
return is_space(ch) or ch == '#';
|
||||
}
|
||||
|
||||
static constexpr bool is_ordinary(int ch)
|
||||
@@ -92,26 +94,7 @@ class sac_parser
|
||||
(ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kAnyPrintMask) != 0);
|
||||
}
|
||||
|
||||
static bool is_unquoted_string(std::string_view text)
|
||||
{
|
||||
bool result = text.empty() or is_ordinary(text.front());
|
||||
|
||||
if (result)
|
||||
{
|
||||
for (auto ch : text)
|
||||
{
|
||||
if (is_non_blank(ch))
|
||||
continue;
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const std::regex kReservedRx(R"(loop_|stop_|global_|data_\S+|save_\S+)", std::regex_constants::icase);
|
||||
|
||||
// but be careful it does not contain e.g. stop_
|
||||
return result and not std::regex_match(text.begin(), text.end(), kReservedRx);
|
||||
}
|
||||
static bool is_unquoted_string(std::string_view text);
|
||||
|
||||
protected:
|
||||
static constexpr uint8_t kCharTraitsTable[128] = {
|
||||
@@ -133,7 +116,8 @@ class sac_parser
|
||||
DATA,
|
||||
LOOP,
|
||||
GLOBAL,
|
||||
SAVE,
|
||||
SAVE_,
|
||||
SAVE_NAME,
|
||||
STOP,
|
||||
Tag,
|
||||
Value
|
||||
@@ -148,7 +132,8 @@ class sac_parser
|
||||
case CIFToken::DATA: return "DATA";
|
||||
case CIFToken::LOOP: return "LOOP";
|
||||
case CIFToken::GLOBAL: return "GLOBAL";
|
||||
case CIFToken::SAVE: return "SAVE";
|
||||
case CIFToken::SAVE_: return "SAVE";
|
||||
case CIFToken::SAVE_NAME: return "SAVE+name";
|
||||
case CIFToken::STOP: return "STOP";
|
||||
case CIFToken::Tag: return "Tag";
|
||||
case CIFToken::Value: return "Value";
|
||||
@@ -156,41 +141,13 @@ class sac_parser
|
||||
}
|
||||
}
|
||||
|
||||
enum class CIFValue
|
||||
{
|
||||
Int,
|
||||
Float,
|
||||
Numeric,
|
||||
String,
|
||||
TextField,
|
||||
Inapplicable,
|
||||
Unknown
|
||||
};
|
||||
|
||||
static constexpr const char *get_value_name(CIFValue type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CIFValue::Int: return "Int";
|
||||
case CIFValue::Float: return "Float";
|
||||
case CIFValue::Numeric: return "Numeric";
|
||||
case CIFValue::String: return "String";
|
||||
case CIFValue::TextField: return "TextField";
|
||||
case CIFValue::Inapplicable: return "Inapplicable";
|
||||
case CIFValue::Unknown: return "Unknown";
|
||||
default: return "Invalid type parameter";
|
||||
}
|
||||
}
|
||||
|
||||
// get_next_char takes a char from the buffer, or if it is empty
|
||||
// from the istream. This function also does carriage/linefeed
|
||||
// translation.
|
||||
// get_next_char takes the next character from the istream.
|
||||
// This function also does carriage/linefeed translation.
|
||||
int get_next_char();
|
||||
|
||||
// Put the last read character back into the istream
|
||||
void retract();
|
||||
|
||||
int restart(int start);
|
||||
|
||||
CIFToken get_next_token();
|
||||
|
||||
void match(CIFToken token);
|
||||
@@ -205,6 +162,9 @@ class sac_parser
|
||||
void parse_file();
|
||||
|
||||
protected:
|
||||
|
||||
sac_parser(std::istream &is, bool init = true);
|
||||
|
||||
void parse_global();
|
||||
|
||||
void parse_datablock();
|
||||
@@ -227,13 +187,14 @@ class sac_parser
|
||||
|
||||
// production methods, these are pure virtual here
|
||||
|
||||
virtual void produce_datablock(const std::string &name) = 0;
|
||||
virtual void produce_category(const std::string &name) = 0;
|
||||
virtual void produce_datablock(std::string_view name) = 0;
|
||||
virtual void produce_category(std::string_view name) = 0;
|
||||
virtual void produce_row() = 0;
|
||||
virtual void produce_item(const std::string &category, const std::string &item, const std::string &value) = 0;
|
||||
virtual void produce_item(std::string_view category, std::string_view item, std::string_view value) = 0;
|
||||
|
||||
protected:
|
||||
enum State
|
||||
|
||||
enum class State
|
||||
{
|
||||
Start,
|
||||
White,
|
||||
@@ -246,23 +207,21 @@ class sac_parser
|
||||
UnquotedString,
|
||||
Tag,
|
||||
TextField,
|
||||
Float = 100,
|
||||
Int = 110,
|
||||
Value = 300,
|
||||
DATA,
|
||||
SAVE
|
||||
TextFieldNL,
|
||||
Reserved,
|
||||
Value
|
||||
};
|
||||
|
||||
std::streambuf &m_source;
|
||||
|
||||
// Parser state
|
||||
bool m_validate;
|
||||
uint32_t m_line_nr;
|
||||
bool m_bol;
|
||||
CIFToken m_lookahead;
|
||||
std::string m_token_value;
|
||||
CIFValue mTokenType;
|
||||
std::vector<int> m_buffer; // retract buffer, used to be a stack<char>
|
||||
|
||||
// token buffer
|
||||
std::vector<char> m_token_buffer;
|
||||
std::string_view m_token_value;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -276,13 +235,13 @@ class parser : public sac_parser
|
||||
{
|
||||
}
|
||||
|
||||
void produce_datablock(const std::string &name) override;
|
||||
void produce_datablock(std::string_view name) override;
|
||||
|
||||
void produce_category(const std::string &name) override;
|
||||
void produce_category(std::string_view name) override;
|
||||
|
||||
void produce_row() override;
|
||||
|
||||
void produce_item(const std::string &category, const std::string &item, const std::string &value) override;
|
||||
void produce_item(std::string_view category, std::string_view item, std::string_view value) override;
|
||||
|
||||
protected:
|
||||
file &m_file;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <valarray>
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#if defined(__cpp_impl_three_way_comparison)
|
||||
#include <compare>
|
||||
#endif
|
||||
|
||||
/// \file cif++/symmetry.hpp
|
||||
/// This file contains code to do symmetry operations based on the
|
||||
/// operations as specified in the International Tables.
|
||||
@@ -180,7 +184,7 @@ class datablock;
|
||||
class cell;
|
||||
class spacegroup;
|
||||
class rtop;
|
||||
class sym_op;
|
||||
struct sym_op;
|
||||
|
||||
|
||||
/// @brief A class that encapsulates the symmetry operations as used in PDB files, i.e. a rotational number and a translation vector
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#define STDOUT_FILENO 1
|
||||
#endif
|
||||
|
||||
#if _MSC_VER
|
||||
#if _WIN32
|
||||
#include <io.h>
|
||||
#define isatty _isatty
|
||||
#else
|
||||
@@ -56,7 +56,6 @@ extern CIFPP_EXPORT int VERBOSE;
|
||||
|
||||
// the git 'build' number
|
||||
std::string get_version_nr();
|
||||
// std::string get_version_date();
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Code helping with terminal i/o
|
||||
|
||||
@@ -228,8 +228,9 @@ class validator_factory
|
||||
|
||||
const validator &operator[](std::string_view dictionary_name);
|
||||
|
||||
const validator &construct_validator(std::string_view name, std::istream &is);
|
||||
|
||||
private:
|
||||
void construct_validator(std::string_view name, std::istream &is);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
||||
120
src/category.cpp
120
src/category.cpp
@@ -670,9 +670,13 @@ void category::set_validator(const validator *v, datablock &db)
|
||||
|
||||
if (missing.empty())
|
||||
m_index = new category_index(this);
|
||||
else if (VERBOSE > 0)
|
||||
std::cerr << "Cannot construct index since the key field" << (missing.size() > 1 ? "s" : "") << " "
|
||||
<< cif::join(missing, ", ") + " in " + m_name + " " + (missing.size() == 1 ? "is" : "are") << " missing" << std::endl;
|
||||
else
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg << "Cannot construct index since the key field" << (missing.size() > 1 ? "s" : "") << " "
|
||||
<< cif::join(missing, ", ") << " in " << m_name << " " << (missing.size() == 1 ? "is" : "are") << " missing" << std::endl;
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1227,23 +1231,37 @@ std::string category::get_unique_id(std::function<std::string(int)> generator)
|
||||
{
|
||||
using namespace cif::literals;
|
||||
|
||||
std::string id_tag = "id";
|
||||
if (m_cat_validator != nullptr and m_cat_validator->m_keys.size() == 1)
|
||||
id_tag = m_cat_validator->m_keys.front();
|
||||
|
||||
// calling size() often is a waste of resources
|
||||
if (m_last_unique_num == 0)
|
||||
m_last_unique_num = static_cast<uint32_t>(size());
|
||||
|
||||
for (;;)
|
||||
std::string result = generator(static_cast<int>(m_last_unique_num++));
|
||||
|
||||
std::string id_tag = "id";
|
||||
if (m_cat_validator != nullptr and m_cat_validator->m_keys.size() == 1)
|
||||
{
|
||||
std::string result = generator(static_cast<int>(m_last_unique_num++));
|
||||
|
||||
if (exists(key(id_tag) == result))
|
||||
continue;
|
||||
|
||||
return result;
|
||||
if (m_index == nullptr and m_cat_validator != nullptr)
|
||||
m_index = new category_index(this);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (m_index->find_by_value({{ id_tag, result }}) == nullptr)
|
||||
break;
|
||||
result = generator(static_cast<int>(m_last_unique_num++));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (not exists(key(id_tag) == result))
|
||||
break;
|
||||
|
||||
result = generator(static_cast<int>(m_last_unique_num++));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void category::update_value(const std::vector<row_handle> &rows, std::string_view tag, std::string_view value)
|
||||
@@ -1694,8 +1712,7 @@ void category::reorder_by_index()
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
size_t write_value(std::ostream &os, std::string_view value, size_t offset, size_t width)
|
||||
size_t write_value(std::ostream &os, std::string_view value, size_t offset, size_t width, bool right_aligned)
|
||||
{
|
||||
if (value.find('\n') != std::string::npos or width == 0 or value.length() > 132) // write as text field
|
||||
{
|
||||
@@ -1719,17 +1736,33 @@ namespace detail
|
||||
}
|
||||
else if (sac_parser::is_unquoted_string(value))
|
||||
{
|
||||
if (right_aligned)
|
||||
{
|
||||
if (value.length() < width)
|
||||
{
|
||||
os << std::string(width - value.length() - 1, ' ');
|
||||
offset += width;
|
||||
}
|
||||
else
|
||||
offset += value.length() + 1;
|
||||
}
|
||||
|
||||
os << value;
|
||||
|
||||
if (value.length() < width)
|
||||
{
|
||||
os << std::string(width - value.length(), ' ');
|
||||
offset += width;
|
||||
}
|
||||
if (right_aligned)
|
||||
os << ' ';
|
||||
else
|
||||
{
|
||||
os << ' ';
|
||||
offset += value.length() + 1;
|
||||
if (value.length() < width)
|
||||
{
|
||||
os << std::string(width - value.length(), ' ');
|
||||
offset += width;
|
||||
}
|
||||
else
|
||||
{
|
||||
os << ' ';
|
||||
offset += value.length() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1823,6 +1856,19 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool
|
||||
// If the first Row has a next, we need a loop_
|
||||
bool needLoop = (m_head->m_next != nullptr);
|
||||
|
||||
std::vector<bool> right_aligned(m_columns.size(), false);
|
||||
|
||||
if (m_cat_validator != nullptr)
|
||||
{
|
||||
for (auto cix : order)
|
||||
{
|
||||
auto &col = m_columns[cix];
|
||||
right_aligned[cix] = col.m_validator != nullptr and
|
||||
col.m_validator->m_type != nullptr and
|
||||
col.m_validator->m_type->m_primitive_type == cif::DDL_PrimitiveType::Numb;
|
||||
}
|
||||
}
|
||||
|
||||
if (needLoop)
|
||||
{
|
||||
os << "loop_" << '\n';
|
||||
@@ -1891,7 +1937,7 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
offset = detail::write_value(os, s, offset, w);
|
||||
offset = detail::write_value(os, s, offset, w, right_aligned[cix]);
|
||||
|
||||
if (offset > 132)
|
||||
{
|
||||
@@ -1919,6 +1965,30 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool
|
||||
|
||||
l += 3;
|
||||
|
||||
size_t width = 1;
|
||||
|
||||
for (auto cix : order)
|
||||
{
|
||||
if (not right_aligned[cix])
|
||||
continue;
|
||||
|
||||
std::string_view s;
|
||||
auto iv = m_head->get(cix);
|
||||
if (iv != nullptr)
|
||||
s = iv->text();
|
||||
|
||||
if (s.empty())
|
||||
s = "?";
|
||||
|
||||
size_t l2 = s.length();
|
||||
|
||||
if (not sac_parser::is_unquoted_string(s))
|
||||
l2 += 2;
|
||||
|
||||
if (width < l2)
|
||||
width = l2;
|
||||
}
|
||||
|
||||
for (uint16_t cix : order)
|
||||
{
|
||||
auto &col = m_columns[cix];
|
||||
@@ -1943,7 +2013,7 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (detail::write_value(os, s, offset, 1) != 0)
|
||||
if (detail::write_value(os, s, offset, width, s.empty() or right_aligned[cix]) != 0)
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
|
||||
{
|
||||
for (auto cmp : impl->m_compounds)
|
||||
{
|
||||
if (cmp->id() == id)
|
||||
if (iequals(cmp->id(), id))
|
||||
{
|
||||
result = cmp;
|
||||
break;
|
||||
@@ -582,12 +582,12 @@ CCP4_compound_factory_impl::CCP4_compound_factory_impl(const fs::path &clibd_mon
|
||||
|
||||
auto &chemComps = m_file["comp_list"]["chem_comp"];
|
||||
|
||||
for (const auto &[group, threeLetterCode] : chemComps.rows<std::string, std::string>("group", "three_letter_code"))
|
||||
for (const auto &[group, comp_id] : chemComps.rows<std::string, std::string>("group", "id"))
|
||||
{
|
||||
if (std::regex_match(group, peptideRx))
|
||||
m_known_peptides.insert(threeLetterCode);
|
||||
m_known_peptides.insert(comp_id);
|
||||
else if (cif::iequals(group, "DNA") or cif::iequals(group, "RNA"))
|
||||
m_known_bases.insert(threeLetterCode);
|
||||
m_known_bases.insert(comp_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,7 +597,7 @@ compound *CCP4_compound_factory_impl::create(const std::string &id)
|
||||
|
||||
auto &cat = m_file["comp_list"]["chem_comp"];
|
||||
|
||||
auto rs = cat.find(cif::key("three_letter_code") == id);
|
||||
auto rs = cat.find(cif::key("id") == id);
|
||||
|
||||
if (rs.size() == 1)
|
||||
{
|
||||
|
||||
@@ -117,7 +117,7 @@ class dictionary_parser : public parser
|
||||
if (not m_collected_item_types)
|
||||
m_collected_item_types = collect_item_types();
|
||||
|
||||
std::string saveFrameName = m_token_value;
|
||||
std::string saveFrameName { m_token_value };
|
||||
|
||||
if (saveFrameName.empty())
|
||||
error("Invalid save frame, should contain more than just 'save_' here");
|
||||
@@ -127,7 +127,7 @@ class dictionary_parser : public parser
|
||||
datablock dict(m_token_value);
|
||||
datablock::iterator cat = dict.end();
|
||||
|
||||
match(CIFToken::SAVE);
|
||||
match(CIFToken::SAVE_NAME);
|
||||
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag)
|
||||
{
|
||||
if (m_lookahead == CIFToken::LOOP)
|
||||
@@ -183,7 +183,7 @@ class dictionary_parser : public parser
|
||||
}
|
||||
}
|
||||
|
||||
match(CIFToken::SAVE);
|
||||
match(CIFToken::SAVE_);
|
||||
|
||||
if (isCategorySaveFrame)
|
||||
{
|
||||
@@ -481,4 +481,11 @@ validator parse_dictionary(std::string_view name, std::istream &is)
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
void extend_dictionary(validator &v, std::istream &is)
|
||||
{
|
||||
file f;
|
||||
dictionary_parser p(v, is, f);
|
||||
p.load_dictionary();
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
|
||||
506
src/parser.cpp
506
src/parser.cpp
@@ -32,7 +32,6 @@
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <stack>
|
||||
|
||||
namespace cif
|
||||
@@ -40,13 +39,152 @@ namespace cif
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class reserved_words_automaton
|
||||
{
|
||||
public:
|
||||
reserved_words_automaton() {}
|
||||
|
||||
enum move_result
|
||||
{
|
||||
undefined,
|
||||
no_keyword,
|
||||
data,
|
||||
global,
|
||||
loop,
|
||||
save,
|
||||
save_plus,
|
||||
stop
|
||||
};
|
||||
|
||||
constexpr bool finished() const
|
||||
{
|
||||
return m_state <= 0;
|
||||
}
|
||||
|
||||
constexpr bool matched() const
|
||||
{
|
||||
return m_state < 0;
|
||||
}
|
||||
|
||||
constexpr move_result move(int ch)
|
||||
{
|
||||
move_result result = undefined;
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case -1: // data_
|
||||
if (sac_parser::is_non_blank(ch))
|
||||
m_seen_trailing_chars = true;
|
||||
else if (m_seen_trailing_chars)
|
||||
result = data;
|
||||
else
|
||||
result = no_keyword;
|
||||
break;
|
||||
|
||||
case -2: // global_
|
||||
result = sac_parser::is_non_blank(ch) ? no_keyword : global;
|
||||
break;
|
||||
|
||||
case -3: // loop_
|
||||
result = sac_parser::is_non_blank(ch) ? no_keyword : loop;
|
||||
break;
|
||||
|
||||
case -4: // save_
|
||||
if (sac_parser::is_non_blank(ch))
|
||||
m_seen_trailing_chars = true;
|
||||
else if (m_seen_trailing_chars)
|
||||
result = save_plus;
|
||||
else
|
||||
result = save;
|
||||
break;
|
||||
|
||||
case -5: // stop_
|
||||
result = sac_parser::is_non_blank(ch) ? no_keyword : stop;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(m_state > 0 and m_state < NODE_COUNT);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (s_dag[m_state].ch == (ch & ~0x20))
|
||||
{
|
||||
m_state = s_dag[m_state].next_match;
|
||||
break;
|
||||
}
|
||||
|
||||
m_state = s_dag[m_state].next_nomatch;
|
||||
|
||||
if (m_state == 0)
|
||||
{
|
||||
result = no_keyword;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (result != undefined)
|
||||
m_state = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr struct node
|
||||
{
|
||||
int16_t ch;
|
||||
int8_t next_match;
|
||||
int8_t next_nomatch;
|
||||
} s_dag[] = {
|
||||
{ 0 },
|
||||
{ 'D', 5, 2 },
|
||||
{ 'G', 9, 3 },
|
||||
{ 'L', 15, 4 },
|
||||
{ 'S', 19, 0 },
|
||||
{ 'A', 6, 0 },
|
||||
{ 'T', 7, 0 },
|
||||
{ 'A', 8, 0 },
|
||||
{ '_', -1, 0 },
|
||||
{ 'L', 10, 0 },
|
||||
{ 'O', 11, 0 },
|
||||
{ 'B', 12, 0 },
|
||||
{ 'A', 13, 0 },
|
||||
{ 'L', 14, 0 },
|
||||
{ '_', -2, 0 },
|
||||
{ 'O', 16, 0},
|
||||
{ 'O', 17, 0 },
|
||||
{ 'P', 18, 0 },
|
||||
{ '_', -3, 0 },
|
||||
{ 'A', 21, 20 },
|
||||
{ 'T', 24, 0 },
|
||||
{ 'V', 22, 0 },
|
||||
{ 'E', 23, 0 },
|
||||
{ '_', -4, 0 },
|
||||
{ 'O', 25, 0 },
|
||||
{ 'P', 26, 0 },
|
||||
{ '_', -5, 0 },
|
||||
};
|
||||
|
||||
static constexpr int NODE_COUNT = sizeof(s_dag) / sizeof(node);
|
||||
|
||||
int m_state = 1;
|
||||
bool m_seen_trailing_chars = false;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
sac_parser::sac_parser(std::istream &is, bool init)
|
||||
: m_source(*is.rdbuf())
|
||||
{
|
||||
m_token_buffer.reserve(8192);
|
||||
|
||||
if (is.rdbuf() == nullptr)
|
||||
throw std::runtime_error("Attempt to read from uninitialised stream");
|
||||
|
||||
m_validate = true;
|
||||
m_line_nr = 1;
|
||||
m_bol = true;
|
||||
|
||||
@@ -54,45 +192,54 @@ sac_parser::sac_parser(std::istream &is, bool init)
|
||||
m_lookahead = get_next_token();
|
||||
}
|
||||
|
||||
bool sac_parser::is_unquoted_string(std::string_view text)
|
||||
{
|
||||
bool result = text.empty() or is_ordinary(text.front());
|
||||
if (result)
|
||||
{
|
||||
reserved_words_automaton automaton;
|
||||
|
||||
for (char ch : text)
|
||||
{
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
automaton.move(ch);
|
||||
}
|
||||
|
||||
if (automaton.matched())
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// get_next_char takes a char from the buffer, or if it is empty
|
||||
// from the istream. This function also does carriage/linefeed
|
||||
// translation.
|
||||
int sac_parser::get_next_char()
|
||||
{
|
||||
int result = std::char_traits<char>::eof();
|
||||
|
||||
if (m_buffer.empty())
|
||||
result = m_source.sbumpc();
|
||||
else
|
||||
{
|
||||
result = m_buffer.back();
|
||||
m_buffer.pop_back();
|
||||
}
|
||||
|
||||
// very simple CR/LF translation into LF
|
||||
if (result == '\r')
|
||||
{
|
||||
int lookahead = m_source.sbumpc();
|
||||
if (lookahead != '\n')
|
||||
m_buffer.push_back(lookahead);
|
||||
result = '\n';
|
||||
}
|
||||
int result = m_source.sbumpc();
|
||||
|
||||
if (result == std::char_traits<char>::eof())
|
||||
m_token_value.push_back(0);
|
||||
m_token_buffer.push_back(0);
|
||||
else
|
||||
m_token_value.push_back(std::char_traits<char>::to_char_type(result));
|
||||
|
||||
if (result == '\n')
|
||||
++m_line_nr;
|
||||
|
||||
if (VERBOSE >= 6)
|
||||
{
|
||||
std::cerr << "get_next_char => ";
|
||||
if (iscntrl(result) or not isprint(result))
|
||||
std::cerr << int(result) << std::endl;
|
||||
else
|
||||
std::cerr << char(result) << std::endl;
|
||||
if (result == '\r')
|
||||
{
|
||||
if (m_source.sgetc() == '\n')
|
||||
m_source.sbumpc();
|
||||
|
||||
++m_line_nr;
|
||||
result = '\n';
|
||||
}
|
||||
else if (result == '\n')
|
||||
++m_line_nr;
|
||||
|
||||
m_token_buffer.push_back(std::char_traits<char>::to_char_type(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -100,44 +247,22 @@ int sac_parser::get_next_char()
|
||||
|
||||
void sac_parser::retract()
|
||||
{
|
||||
assert(not m_token_value.empty());
|
||||
assert(not m_token_buffer.empty());
|
||||
|
||||
char ch = m_token_value.back();
|
||||
char ch = m_token_buffer.back();
|
||||
if (ch == '\n')
|
||||
--m_line_nr;
|
||||
|
||||
m_buffer.push_back(ch == 0 ? std::char_traits<char>::eof() : std::char_traits<char>::to_int_type(ch));
|
||||
m_token_value.pop_back();
|
||||
}
|
||||
|
||||
int sac_parser::restart(int start)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
while (not m_token_value.empty())
|
||||
retract();
|
||||
|
||||
switch (start)
|
||||
if (ch != 0)
|
||||
{
|
||||
case State::Start:
|
||||
result = State::Float;
|
||||
break;
|
||||
// since we always putback at most a single character,
|
||||
// the test below should never fail.
|
||||
|
||||
case State::Float:
|
||||
result = State::Int;
|
||||
break;
|
||||
|
||||
case State::Int:
|
||||
result = State::Value;
|
||||
break;
|
||||
|
||||
default:
|
||||
error("Invalid state in SacParser");
|
||||
if (m_source.sputbackc(ch) == std::char_traits<char>::eof())
|
||||
throw std::runtime_error("putback failure");
|
||||
}
|
||||
|
||||
m_bol = false;
|
||||
|
||||
return result;
|
||||
m_token_buffer.pop_back();
|
||||
}
|
||||
|
||||
sac_parser::CIFToken sac_parser::get_next_token()
|
||||
@@ -146,11 +271,13 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
|
||||
CIFToken result = CIFToken::Unknown;
|
||||
int quoteChar = 0;
|
||||
int state = State::Start, start = State::Start;
|
||||
State state = State::Start;
|
||||
m_bol = false;
|
||||
|
||||
m_token_value.clear();
|
||||
mTokenType = CIFValue::Unknown;
|
||||
m_token_buffer.clear();
|
||||
m_token_value = {};
|
||||
|
||||
reserved_words_automaton dag;
|
||||
|
||||
while (result == CIFToken::Unknown)
|
||||
{
|
||||
@@ -174,23 +301,27 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
state = State::Tag;
|
||||
else if (ch == ';' and m_bol)
|
||||
state = State::TextField;
|
||||
else if (ch == '?')
|
||||
state = State::QuestionMark;
|
||||
else if (ch == '\'' or ch == '"')
|
||||
{
|
||||
quoteChar = ch;
|
||||
state = State::QuotedString;
|
||||
}
|
||||
else if (dag.move(ch) == reserved_words_automaton::undefined)
|
||||
state = State::Reserved;
|
||||
else
|
||||
state = start = restart(start);
|
||||
state = State::Value;
|
||||
break;
|
||||
|
||||
case State::White:
|
||||
if (ch == kEOF)
|
||||
result = CIFToken::Eof;
|
||||
else if (not isspace(ch))
|
||||
else if (not is_space(ch))
|
||||
{
|
||||
state = State::Start;
|
||||
retract();
|
||||
m_token_value.clear();
|
||||
m_token_buffer.clear();
|
||||
}
|
||||
else
|
||||
m_bol = (ch == '\n');
|
||||
@@ -201,38 +332,40 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
{
|
||||
state = State::Start;
|
||||
m_bol = true;
|
||||
m_token_value.clear();
|
||||
m_token_buffer.clear();
|
||||
}
|
||||
else if (ch == kEOF)
|
||||
result = CIFToken::Eof;
|
||||
else if (not is_any_print(ch))
|
||||
error("invalid character in comment");
|
||||
break;
|
||||
|
||||
case State::QuestionMark:
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::Value;
|
||||
}
|
||||
else
|
||||
state = State::Value;
|
||||
break;
|
||||
|
||||
case State::TextField:
|
||||
if (ch == '\n')
|
||||
state = State::TextField + 1;
|
||||
state = State::TextFieldNL;
|
||||
else if (ch == kEOF)
|
||||
error("unterminated textfield");
|
||||
// else if (ch == '\\')
|
||||
// state = State::Esc;
|
||||
else if (not is_any_print(ch) and cif::VERBOSE > 2)
|
||||
warning("invalid character in text field '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")");
|
||||
break;
|
||||
|
||||
// case State::Esc:
|
||||
// if (ch == '\n')
|
||||
|
||||
// break;
|
||||
|
||||
case State::TextField + 1:
|
||||
case State::TextFieldNL:
|
||||
if (is_text_lead(ch) or ch == ' ' or ch == '\t')
|
||||
state = State::TextField;
|
||||
else if (ch == ';')
|
||||
{
|
||||
assert(m_token_value.length() >= 2);
|
||||
m_token_value = m_token_value.substr(1, m_token_value.length() - 3);
|
||||
mTokenType = CIFValue::TextField;
|
||||
assert(m_token_buffer.size() >= 2);
|
||||
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3);
|
||||
result = CIFToken::Value;
|
||||
}
|
||||
else if (ch == kEOF)
|
||||
@@ -255,12 +388,10 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::Value;
|
||||
mTokenType = CIFValue::String;
|
||||
|
||||
if (m_token_value.length() < 2)
|
||||
if (m_token_buffer.size() < 2)
|
||||
error("Invalid quoted string token");
|
||||
|
||||
m_token_value = m_token_value.substr(1, m_token_value.length() - 2);
|
||||
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 2);
|
||||
}
|
||||
else if (ch == quoteChar)
|
||||
;
|
||||
@@ -277,149 +408,68 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::Tag;
|
||||
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
|
||||
}
|
||||
break;
|
||||
|
||||
case State::Float:
|
||||
if (ch == '+' or ch == '-')
|
||||
case State::Reserved:
|
||||
switch (dag.move(ch))
|
||||
{
|
||||
state = State::Float + 1;
|
||||
case reserved_words_automaton::undefined:
|
||||
break;
|
||||
|
||||
case reserved_words_automaton::no_keyword:
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::Value;
|
||||
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
|
||||
}
|
||||
else
|
||||
state = State::Value;
|
||||
break;
|
||||
|
||||
case reserved_words_automaton::data:
|
||||
retract();
|
||||
m_token_value = std::string_view(m_token_buffer.data() + 5, m_token_buffer.size() - 5);
|
||||
result = CIFToken::DATA;
|
||||
break;
|
||||
|
||||
case reserved_words_automaton::global:
|
||||
retract();
|
||||
result = CIFToken::GLOBAL;
|
||||
break;
|
||||
|
||||
case reserved_words_automaton::loop:
|
||||
retract();
|
||||
result = CIFToken::LOOP;
|
||||
break;
|
||||
|
||||
case reserved_words_automaton::save:
|
||||
retract();
|
||||
result = CIFToken::SAVE_;
|
||||
break;
|
||||
|
||||
case reserved_words_automaton::save_plus:
|
||||
retract();
|
||||
m_token_value = std::string_view(m_token_buffer.data() + 5, m_token_buffer.size() - 5);
|
||||
result = CIFToken::SAVE_NAME;
|
||||
break;
|
||||
|
||||
case reserved_words_automaton::stop:
|
||||
retract();
|
||||
result = CIFToken::STOP;
|
||||
break;
|
||||
}
|
||||
else if (isdigit(ch))
|
||||
state = State::Float + 1;
|
||||
else
|
||||
state = start = restart(start);
|
||||
break;
|
||||
|
||||
case State::Float + 1:
|
||||
// if (ch == '(') // numeric???
|
||||
// mState = State::NumericSuffix;
|
||||
// else
|
||||
if (ch == '.')
|
||||
state = State::Float + 2;
|
||||
else if (tolower(ch) == 'e')
|
||||
state = State::Float + 3;
|
||||
else if (is_white(ch) or ch == kEOF)
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::Value;
|
||||
mTokenType = CIFValue::Int;
|
||||
}
|
||||
else
|
||||
state = start = restart(start);
|
||||
break;
|
||||
|
||||
// parsed '.'
|
||||
case State::Float + 2:
|
||||
if (tolower(ch) == 'e')
|
||||
state = State::Float + 3;
|
||||
else if (is_white(ch) or ch == kEOF)
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::Value;
|
||||
mTokenType = CIFValue::Float;
|
||||
}
|
||||
else
|
||||
state = start = restart(start);
|
||||
break;
|
||||
|
||||
// parsed 'e'
|
||||
case State::Float + 3:
|
||||
if (ch == '-' or ch == '+')
|
||||
state = State::Float + 4;
|
||||
else if (isdigit(ch))
|
||||
state = State::Float + 5;
|
||||
else
|
||||
state = start = restart(start);
|
||||
break;
|
||||
|
||||
case State::Float + 4:
|
||||
if (isdigit(ch))
|
||||
state = State::Float + 5;
|
||||
else
|
||||
state = start = restart(start);
|
||||
break;
|
||||
|
||||
case State::Float + 5:
|
||||
if (is_white(ch) or ch == kEOF)
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::Value;
|
||||
mTokenType = CIFValue::Float;
|
||||
}
|
||||
else
|
||||
state = start = restart(start);
|
||||
break;
|
||||
|
||||
case State::Int:
|
||||
if (isdigit(ch) or ch == '+' or ch == '-')
|
||||
state = State::Int + 1;
|
||||
else
|
||||
state = start = restart(start);
|
||||
break;
|
||||
|
||||
case State::Int + 1:
|
||||
if (is_white(ch) or ch == kEOF)
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::Value;
|
||||
mTokenType = CIFValue::Int;
|
||||
}
|
||||
else
|
||||
state = start = restart(start);
|
||||
break;
|
||||
|
||||
case State::Value:
|
||||
if (ch == '_')
|
||||
{
|
||||
std::string s = to_lower_copy(m_token_value);
|
||||
|
||||
if (s == "data_")
|
||||
{
|
||||
state = State::DATA;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s == "save_")
|
||||
{
|
||||
state = State::SAVE;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == CIFToken::Unknown and not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::Value;
|
||||
|
||||
if (m_token_value == ".")
|
||||
mTokenType = CIFValue::Inapplicable;
|
||||
else if (iequals(m_token_value, "global_"))
|
||||
result = CIFToken::GLOBAL;
|
||||
else if (iequals(m_token_value, "stop_"))
|
||||
result = CIFToken::STOP;
|
||||
else if (iequals(m_token_value, "loop_"))
|
||||
result = CIFToken::LOOP;
|
||||
else if (m_token_value == "?")
|
||||
{
|
||||
mTokenType = CIFValue::Unknown;
|
||||
m_token_value.clear();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case State::DATA:
|
||||
case State::SAVE:
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
|
||||
if (state == State::DATA)
|
||||
result = CIFToken::DATA;
|
||||
else
|
||||
result = CIFToken::SAVE;
|
||||
|
||||
m_token_value.erase(m_token_value.begin(), m_token_value.begin() + 5);
|
||||
result = CIFToken::Value;
|
||||
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -433,8 +483,6 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
if (VERBOSE >= 5)
|
||||
{
|
||||
std::cerr << get_token_name(result);
|
||||
if (mTokenType != CIFValue::Unknown)
|
||||
std::cerr << ' ' << get_value_name(mTokenType);
|
||||
if (result != CIFToken::Eof)
|
||||
std::cerr << " " << std::quoted(m_token_value);
|
||||
std::cerr << std::endl;
|
||||
@@ -506,7 +554,7 @@ bool sac_parser::parse_single_datablock(const std::string &datablock)
|
||||
break;
|
||||
|
||||
case string_quote:
|
||||
if (std::isspace(ch))
|
||||
if (is_space(ch))
|
||||
state = start;
|
||||
else
|
||||
state = string;
|
||||
@@ -518,7 +566,7 @@ bool sac_parser::parse_single_datablock(const std::string &datablock)
|
||||
break;
|
||||
|
||||
case data:
|
||||
if (isspace(ch) and dblk[si] == 0)
|
||||
if (is_space(ch) and dblk[si] == 0)
|
||||
found = true;
|
||||
else if (dblk[si++] != ch)
|
||||
state = start;
|
||||
@@ -596,7 +644,7 @@ sac_parser::datablock_index sac_parser::index_datablocks()
|
||||
break;
|
||||
|
||||
case string_quote:
|
||||
if (std::isspace(ch))
|
||||
if (is_space(ch))
|
||||
state = start;
|
||||
else
|
||||
state = string;
|
||||
@@ -620,7 +668,7 @@ sac_parser::datablock_index sac_parser::index_datablocks()
|
||||
case data_name:
|
||||
if (is_non_blank(ch))
|
||||
datablock.insert(datablock.end(), char(ch));
|
||||
else if (isspace(ch))
|
||||
else if (is_space(ch))
|
||||
{
|
||||
if (not datablock.empty())
|
||||
index[datablock] = m_source.pubseekoff(0, std::ios_base::cur, std::ios_base::in);
|
||||
@@ -696,7 +744,7 @@ void sac_parser::parse_datablock()
|
||||
static const std::string kUnitializedCategory("<invalid>");
|
||||
std::string cat = kUnitializedCategory; // intial value acts as a guard for empty category names
|
||||
|
||||
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE)
|
||||
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE_NAME)
|
||||
{
|
||||
switch (m_lookahead)
|
||||
{
|
||||
@@ -761,7 +809,7 @@ void sac_parser::parse_datablock()
|
||||
break;
|
||||
}
|
||||
|
||||
case CIFToken::SAVE:
|
||||
case CIFToken::SAVE_NAME:
|
||||
parse_save_frame();
|
||||
break;
|
||||
|
||||
@@ -779,7 +827,7 @@ void sac_parser::parse_save_frame()
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
void parser::produce_datablock(const std::string &name)
|
||||
void parser::produce_datablock(std::string_view name)
|
||||
{
|
||||
if (VERBOSE >= 4)
|
||||
std::cerr << "producing data_" << name << std::endl;
|
||||
@@ -788,7 +836,7 @@ void parser::produce_datablock(const std::string &name)
|
||||
m_datablock = &(*iter);
|
||||
}
|
||||
|
||||
void parser::produce_category(const std::string &name)
|
||||
void parser::produce_category(std::string_view name)
|
||||
{
|
||||
if (VERBOSE >= 4)
|
||||
std::cerr << "producing category " << name << std::endl;
|
||||
@@ -810,7 +858,7 @@ void parser::produce_row()
|
||||
// m_row.lineNr(m_line_nr);
|
||||
}
|
||||
|
||||
void parser::produce_item(const std::string &category, const std::string &item, const std::string &value)
|
||||
void parser::produce_item(std::string_view category, std::string_view item, std::string_view value)
|
||||
{
|
||||
if (VERBOSE >= 4)
|
||||
std::cerr << "producing _" << category << '.' << item << " -> " << value << std::endl;
|
||||
@@ -821,4 +869,4 @@ void parser::produce_item(const std::string &category, const std::string &item,
|
||||
m_row[item] = m_token_value;
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
} // namespace cif
|
||||
|
||||
@@ -1170,6 +1170,8 @@ void PDBFileParser::PreParseInput(std::istream &is)
|
||||
std::string value;
|
||||
if (lookahead.length() > 6)
|
||||
value = cif::trim_right_copy(lookahead.substr(6));
|
||||
|
||||
lookahead.clear();
|
||||
|
||||
uint32_t curLineNr = lineNr;
|
||||
getline(is, lookahead);
|
||||
|
||||
@@ -61,7 +61,7 @@ quaternion_type<T> normalize(quaternion_type<T> q)
|
||||
|
||||
quaternion construct_from_angle_axis(float angle, point axis)
|
||||
{
|
||||
angle = (angle * kPI / 180) / 2;
|
||||
angle = static_cast<float>((angle * kPI / 180) / 2);
|
||||
auto s = std::sin(angle);
|
||||
auto c = std::cos(angle);
|
||||
|
||||
@@ -119,7 +119,7 @@ point center_points(std::vector<point> &Points)
|
||||
}
|
||||
|
||||
quaternion construct_for_dihedral_angle(point p1, point p2, point p3, point p4,
|
||||
float angle, float esd)
|
||||
float angle, float /*esd*/)
|
||||
{
|
||||
p1 -= p3;
|
||||
p2 -= p3;
|
||||
|
||||
@@ -70,12 +70,12 @@ void cell::init()
|
||||
|
||||
m_orthogonal = identity_matrix(3);
|
||||
|
||||
m_orthogonal(0, 0) = m_a;
|
||||
m_orthogonal(0, 1) = m_b * std::cos(gamma);
|
||||
m_orthogonal(0, 2) = m_c * std::cos(beta);
|
||||
m_orthogonal(1, 1) = m_b * std::sin(gamma);
|
||||
m_orthogonal(1, 2) = -m_c * std::sin(beta) * std::cos(alpha_star);
|
||||
m_orthogonal(2, 2) = m_c * std::sin(beta) * std::sin(alpha_star);
|
||||
m_orthogonal(0, 0) = static_cast<float>(m_a);
|
||||
m_orthogonal(0, 1) = static_cast<float>(m_b * std::cos(gamma));
|
||||
m_orthogonal(0, 2) = static_cast<float>(m_c * std::cos(beta));
|
||||
m_orthogonal(1, 1) = static_cast<float>(m_b * std::sin(gamma));
|
||||
m_orthogonal(1, 2) = static_cast<float>(-m_c * std::sin(beta) * std::cos(alpha_star));
|
||||
m_orthogonal(2, 2) = static_cast<float>(m_c * std::sin(beta) * std::sin(alpha_star));
|
||||
|
||||
m_fractional = inverse(m_orthogonal);
|
||||
}
|
||||
@@ -90,7 +90,7 @@ sym_op::sym_op(std::string_view s)
|
||||
int rnri = 256; // default to unexisting number
|
||||
auto r = std::from_chars(b, e, rnri);
|
||||
|
||||
m_nr = rnri;
|
||||
m_nr = static_cast<uint8_t>(rnri);
|
||||
m_ta = r.ptr[1] - '0';
|
||||
m_tb = r.ptr[2] - '0';
|
||||
m_tc = r.ptr[3] - '0';
|
||||
@@ -121,21 +121,21 @@ transformation::transformation(const symop_data &data)
|
||||
{
|
||||
const auto &d = data.data();
|
||||
|
||||
m_rotation(0, 0) = d[0];
|
||||
m_rotation(0, 1) = d[1];
|
||||
m_rotation(0, 2) = d[2];
|
||||
m_rotation(1, 0) = d[3];
|
||||
m_rotation(1, 1) = d[4];
|
||||
m_rotation(1, 2) = d[5];
|
||||
m_rotation(2, 0) = d[6];
|
||||
m_rotation(2, 1) = d[7];
|
||||
m_rotation(2, 2) = d[8];
|
||||
m_rotation(0, 0) = static_cast<float>(d[0]);
|
||||
m_rotation(0, 1) = static_cast<float>(d[1]);
|
||||
m_rotation(0, 2) = static_cast<float>(d[2]);
|
||||
m_rotation(1, 0) = static_cast<float>(d[3]);
|
||||
m_rotation(1, 1) = static_cast<float>(d[4]);
|
||||
m_rotation(1, 2) = static_cast<float>(d[5]);
|
||||
m_rotation(2, 0) = static_cast<float>(d[6]);
|
||||
m_rotation(2, 1) = static_cast<float>(d[7]);
|
||||
m_rotation(2, 2) = static_cast<float>(d[8]);
|
||||
|
||||
try_create_quaternion();
|
||||
|
||||
m_translation.m_x = d[9] == 0 ? 0 : 1.0 * d[9] / d[10];
|
||||
m_translation.m_y = d[11] == 0 ? 0 : 1.0 * d[11] / d[12];
|
||||
m_translation.m_z = d[13] == 0 ? 0 : 1.0 * d[13] / d[14];
|
||||
m_translation.m_x = static_cast<float>(d[9] == 0 ? 0 : 1.0 * d[9] / d[10]);
|
||||
m_translation.m_y = static_cast<float>(d[11] == 0 ? 0 : 1.0 * d[11] / d[12]);
|
||||
m_translation.m_z = static_cast<float>(d[13] == 0 ? 0 : 1.0 * d[13] / d[14]);
|
||||
}
|
||||
|
||||
transformation::transformation(const matrix3x3<float> &r, const cif::point &t)
|
||||
@@ -461,7 +461,7 @@ std::tuple<float,point,sym_op> crystal::closest_symmetry_copy(point a, point b)
|
||||
|
||||
for (size_t i = 0; i < m_spacegroup.size(); ++i)
|
||||
{
|
||||
sym_op s(i + 1);
|
||||
sym_op s(static_cast<uint8_t>(i + 1));
|
||||
auto &t = m_spacegroup[i];
|
||||
|
||||
auto fsb = t(fb);
|
||||
|
||||
25
src/text.cpp
25
src/text.cpp
@@ -236,28 +236,19 @@ std::string cif_id_for_number(int number)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (number >= 26 * 26 * 26)
|
||||
result = 'L' + std::to_string(number);
|
||||
else
|
||||
do
|
||||
{
|
||||
if (number >= 26 * 26)
|
||||
{
|
||||
int v = number / (26 * 26);
|
||||
result += char('A' - 1 + v);
|
||||
number %= (26 * 26);
|
||||
}
|
||||
int r = number % 26;
|
||||
result += static_cast<char>('A' + r);
|
||||
|
||||
if (number >= 26)
|
||||
{
|
||||
int v = number / 26;
|
||||
result += char('A' - 1 + v);
|
||||
number %= 26;
|
||||
}
|
||||
|
||||
result += char('A' + number);
|
||||
number = (number - r) / 26 - 1;
|
||||
}
|
||||
while (number >= 0);
|
||||
|
||||
std::reverse(result.begin(), result.end());
|
||||
|
||||
assert(not result.empty());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,10 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#if not defined(_MSC_VER)
|
||||
#if not defined(_WIN32)
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
@@ -69,7 +68,7 @@ std::string get_version_nr()
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifdef _WIN32
|
||||
}
|
||||
#include <Windows.h>
|
||||
#include <libloaderapi.h>
|
||||
@@ -161,6 +160,8 @@ struct progress_bar_impl
|
||||
void print_progress();
|
||||
void print_done();
|
||||
|
||||
using time_point = std::chrono::time_point<std::chrono::system_clock>;
|
||||
|
||||
int64_t m_max_value;
|
||||
std::atomic<int64_t> m_consumed;
|
||||
int64_t m_last_consumed = 0;
|
||||
@@ -168,8 +169,8 @@ struct progress_bar_impl
|
||||
std::string m_action, m_message;
|
||||
std::mutex m_mutex;
|
||||
std::thread m_thread;
|
||||
std::chrono::time_point<std::chrono::system_clock>
|
||||
m_start = std::chrono::system_clock::now();
|
||||
time_point m_start = std::chrono::system_clock::now();
|
||||
time_point m_last = std::chrono::system_clock::now();
|
||||
bool m_stop = false;
|
||||
};
|
||||
|
||||
@@ -192,7 +193,9 @@ void progress_bar_impl::run()
|
||||
{
|
||||
while (not m_stop)
|
||||
{
|
||||
if (std::chrono::system_clock::now() - m_start < 2s)
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
||||
if (now - m_start < 2s or now - m_last < 100ms)
|
||||
{
|
||||
std::this_thread::sleep_for(10ms);
|
||||
continue;
|
||||
@@ -201,11 +204,12 @@ void progress_bar_impl::run()
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (not printedAny and isatty(STDOUT_FILENO))
|
||||
std::cout << "\e[?25l";
|
||||
std::cout << "\x1b[?25l";
|
||||
|
||||
print_progress();
|
||||
|
||||
printedAny = true;
|
||||
m_last = std::chrono::system_clock::now();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -216,7 +220,7 @@ void progress_bar_impl::run()
|
||||
{
|
||||
print_done();
|
||||
if (isatty(STDOUT_FILENO))
|
||||
std::cout << "\e[?25h";
|
||||
std::cout << "\x1b[?25h";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,11 +411,19 @@ struct rsrc_imp
|
||||
};
|
||||
} // namespace mrsrc
|
||||
|
||||
#if _MSC_VER
|
||||
#if _WIN32
|
||||
|
||||
extern "C" CIFPP_EXPORT const mrsrc::rsrc_imp *gResourceIndexDefault[1] = {};
|
||||
extern "C" CIFPP_EXPORT const char *gResourceDataDefault[1] = {};
|
||||
extern "C" CIFPP_EXPORT const char *gResourceNameDefault[1] = {};
|
||||
#if __MINGW32__
|
||||
|
||||
extern "C" __attribute__((weak, alias("gResourceIndexDefault"))) const mrsrc::rsrc_imp gResourceIndex[];
|
||||
extern "C" __attribute__((weak, alias("gResourceDataDefault"))) const char gResourceData[];
|
||||
extern "C" __attribute__((weak, alias("gResourceNameDefault"))) const char gResourceName[];
|
||||
|
||||
#else
|
||||
|
||||
extern "C" const mrsrc::rsrc_imp *gResourceIndexDefault[1] = {};
|
||||
extern "C" const char *gResourceDataDefault[1] = {};
|
||||
extern "C" const char *gResourceNameDefault[1] = {};
|
||||
|
||||
extern "C" const mrsrc::rsrc_imp gResourceIndex[];
|
||||
extern "C" const char gResourceData[];
|
||||
@@ -421,6 +433,8 @@ extern "C" const char gResourceName[];
|
||||
#pragma comment(linker, "/alternatename:gResourceData=gResourceDataDefault")
|
||||
#pragma comment(linker, "/alternatename:gResourceName=gResourceNameDefault")
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
extern const __attribute__((weak)) mrsrc::rsrc_imp gResourceIndex[];
|
||||
extern const __attribute__((weak)) char gResourceData[];
|
||||
|
||||
@@ -491,9 +491,9 @@ const validator &validator_factory::operator[](std::string_view dictionary_name)
|
||||
}
|
||||
}
|
||||
|
||||
void validator_factory::construct_validator(std::string_view name, std::istream &is)
|
||||
const validator &validator_factory::construct_validator(std::string_view name, std::istream &is)
|
||||
{
|
||||
m_validators.emplace_back(parse_dictionary(name, is));
|
||||
return m_validators.emplace_back(parse_dictionary(name, is));
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
|
||||
187
test/REA_v2.cif
Normal file
187
test/REA_v2.cif
Normal file
@@ -0,0 +1,187 @@
|
||||
data_REA_v2
|
||||
#
|
||||
_chem_comp.id REA_v2
|
||||
_chem_comp.name "RETINOIC ACID"
|
||||
_chem_comp.type NON-POLYMER
|
||||
_chem_comp.pdbx_type HETAIN
|
||||
_chem_comp.formula "C20 H28 O2"
|
||||
_chem_comp.mon_nstd_parent_comp_id ?
|
||||
_chem_comp.pdbx_synonyms ?
|
||||
_chem_comp.pdbx_formal_charge 0
|
||||
_chem_comp.pdbx_initial_date 1999-07-08
|
||||
_chem_comp.pdbx_modified_date 2016-10-18
|
||||
_chem_comp.pdbx_ambiguous_flag N
|
||||
_chem_comp.pdbx_release_status REL
|
||||
_chem_comp.pdbx_replaced_by ?
|
||||
_chem_comp.pdbx_replaces 3KV
|
||||
_chem_comp.formula_weight 300.435
|
||||
_chem_comp.one_letter_code ?
|
||||
_chem_comp.pdbx_model_coordinates_details ?
|
||||
_chem_comp.pdbx_model_coordinates_missing_flag N
|
||||
_chem_comp.pdbx_ideal_coordinates_details Corina
|
||||
_chem_comp.pdbx_ideal_coordinates_missing_flag N
|
||||
_chem_comp.pdbx_model_coordinates_db_code 1CBS
|
||||
_chem_comp.pdbx_subcomponent_list ?
|
||||
_chem_comp.pdbx_processing_site RCSB
|
||||
#
|
||||
loop_
|
||||
_chem_comp_atom.comp_id
|
||||
_chem_comp_atom.atom_id
|
||||
_chem_comp_atom.alt_atom_id
|
||||
_chem_comp_atom.type_symbol
|
||||
_chem_comp_atom.charge
|
||||
_chem_comp_atom.pdbx_align
|
||||
_chem_comp_atom.pdbx_aromatic_flag
|
||||
_chem_comp_atom.pdbx_leaving_atom_flag
|
||||
_chem_comp_atom.pdbx_stereo_config
|
||||
_chem_comp_atom.model_Cartn_x
|
||||
_chem_comp_atom.model_Cartn_y
|
||||
_chem_comp_atom.model_Cartn_z
|
||||
_chem_comp_atom.pdbx_model_Cartn_x_ideal
|
||||
_chem_comp_atom.pdbx_model_Cartn_y_ideal
|
||||
_chem_comp_atom.pdbx_model_Cartn_z_ideal
|
||||
_chem_comp_atom.pdbx_component_atom_id
|
||||
_chem_comp_atom.pdbx_component_comp_id
|
||||
_chem_comp_atom.pdbx_ordinal
|
||||
REA_v2 C1 C1 C 0 1 N N N 21.972 29.831 16.739 -4.684 0.932 -0.497 C1 REA_v2 1
|
||||
REA_v2 C2 C2 C 0 1 N N N 20.921 30.524 15.841 -5.837 0.190 -1.176 C2 REA_v2 2
|
||||
REA_v2 C3 C3 C 0 1 N N N 20.245 29.635 14.848 -6.441 -0.798 -0.171 C3 REA_v2 3
|
||||
REA_v2 C4 C4 C 0 1 N N N 19.555 28.479 15.488 -5.418 -1.903 0.100 C4 REA_v2 4
|
||||
REA_v2 C5 C5 C 0 1 N N N 20.389 27.812 16.587 -4.082 -1.301 0.429 C5 REA_v2 5
|
||||
REA_v2 C6 C6 C 0 1 N N N 21.425 28.446 17.218 -3.756 -0.048 0.161 C6 REA_v2 6
|
||||
REA_v2 C7 C7 C 0 1 N N N 22.242 27.851 18.297 -2.457 0.396 0.516 C7 REA_v2 7
|
||||
REA_v2 C8 C8 C 0 1 N N N 21.868 26.977 19.240 -1.363 -0.229 0.007 C8 REA_v2 8
|
||||
REA_v2 C9 C9 C 0 1 N N N 22.705 26.434 20.286 -0.076 0.257 0.298 C9 REA_v2 9
|
||||
REA_v2 C10 C10 C 0 1 N N N 22.159 25.536 21.131 1.022 -0.370 -0.213 C10 REA_v2 10
|
||||
REA_v2 C11 C11 C 0 1 N N N 22.875 24.924 22.234 2.306 0.115 0.077 C11 REA_v2 11
|
||||
REA_v2 C12 C12 C 0 1 N N N 22.237 24.026 22.990 3.405 -0.513 -0.435 C12 REA_v2 12
|
||||
REA_v2 C13 C13 C 0 1 N N N 22.856 23.377 24.125 4.689 -0.028 -0.144 C13 REA_v2 13
|
||||
REA_v2 C14 C14 C 0 1 N N N 22.135 22.473 24.834 5.787 -0.655 -0.656 C14 REA_v2 14
|
||||
REA_v2 C15 C15 C 0 1 N N N 22.563 21.710 26.016 7.077 -0.265 -0.244 C15 REA_v2 15
|
||||
REA_v2 C16 C16 C 0 1 N N N 22.238 30.737 17.948 -5.246 1.886 0.559 C16 REA_v2 16
|
||||
REA_v2 C17 C17 C 0 1 N N N 23.292 29.620 15.948 -3.911 1.737 -1.544 C17 REA_v2 17
|
||||
REA_v2 C18 C18 C 0 1 N N N 19.791 26.449 16.947 -3.056 -2.175 1.103 C18 REA_v2 18
|
||||
REA_v2 C19 C19 C 0 1 N N N 24.181 26.841 20.385 0.090 1.471 1.175 C19 REA_v2 19
|
||||
REA_v2 C20 C20 C 0 1 N N N 24.303 23.747 24.489 4.855 1.186 0.733 C20 REA_v2 20
|
||||
REA_v2 O1 O1 O 0 1 N N N 23.640 21.075 25.978 7.210 0.553 0.648 O1 REA_v2 21
|
||||
REA_v2 O2 O2 O 0 1 N N N 21.840 21.712 27.037 8.166 -0.798 -0.840 O2 REA_v2 22
|
||||
REA_v2 H21 H21 H 0 1 N N N 20.147 30.955 16.494 -6.598 0.905 -1.490 H21 REA_v2 23
|
||||
REA_v2 H22 H22 H 0 1 N N N 21.425 31.330 15.288 -5.462 -0.353 -2.044 H22 REA_v2 24
|
||||
REA_v2 H31 H31 H 0 1 N N N 19.501 30.227 14.295 -6.673 -0.278 0.759 H31 REA_v2 25
|
||||
REA_v2 H32 H32 H 0 1 N N N 21.001 29.250 14.148 -7.349 -1.234 -0.586 H32 REA_v2 26
|
||||
REA_v2 H41 H41 H 0 1 N N N 18.613 28.835 15.931 -5.756 -2.511 0.938 H41 REA_v2 27
|
||||
REA_v2 H42 H42 H 0 1 N N N 19.335 27.730 14.713 -5.322 -2.531 -0.786 H42 REA_v2 28
|
||||
REA_v2 H7 H7 H 0 1 N N N 23.276 28.162 18.329 -2.337 1.230 1.191 H7 REA_v2 29
|
||||
REA_v2 H8 H8 H 0 1 N N N 20.840 26.645 19.217 -1.482 -1.100 -0.622 H8 REA_v2 30
|
||||
REA_v2 H10 H10 H 0 1 N N N 21.127 25.256 20.977 0.903 -1.241 -0.842 H10 REA_v2 31
|
||||
REA_v2 H11 H11 H 0 1 N N N 23.902 25.189 22.440 2.425 0.985 0.706 H11 REA_v2 32
|
||||
REA_v2 H12 H12 H 0 1 N N N 21.216 23.774 22.743 3.286 -1.383 -1.063 H12 REA_v2 33
|
||||
REA_v2 H14 H14 H 0 1 N N N 21.127 22.292 24.490 5.667 -1.451 -1.376 H14 REA_v2 34
|
||||
REA_v2 H161 H161 H 0 0 N N N 22.984 30.265 18.604 -5.802 1.316 1.303 H161 REA_v2 35
|
||||
REA_v2 H162 H162 H 0 0 N N N 22.618 31.709 17.601 -4.426 2.415 1.044 H162 REA_v2 36
|
||||
REA_v2 H163 H163 H 0 0 N N N 21.302 30.887 18.506 -5.911 2.605 0.081 H163 REA_v2 37
|
||||
REA_v2 H171 H171 H 0 0 N N N 24.033 29.127 16.595 -4.598 2.394 -2.077 H171 REA_v2 38
|
||||
REA_v2 H172 H172 H 0 0 N N N 23.095 28.989 15.069 -3.146 2.335 -1.050 H172 REA_v2 39
|
||||
REA_v2 H173 H173 H 0 0 N N N 23.683 30.595 15.620 -3.439 1.054 -2.251 H173 REA_v2 40
|
||||
REA_v2 H181 H181 H 0 0 N N N 20.397 25.979 17.736 -3.448 -3.187 1.201 H181 REA_v2 41
|
||||
REA_v2 H182 H182 H 0 0 N N N 18.761 26.584 17.308 -2.145 -2.194 0.503 H182 REA_v2 42
|
||||
REA_v2 H183 H183 H 0 0 N N N 19.786 25.804 16.056 -2.831 -1.775 2.092 H183 REA_v2 43
|
||||
REA_v2 H191 H191 H 0 0 N N N 24.647 26.327 21.238 0.171 1.159 2.216 H191 REA_v2 44
|
||||
REA_v2 H192 H192 H 0 0 N N N 24.702 26.559 19.458 0.993 2.008 0.885 H192 REA_v2 45
|
||||
REA_v2 H193 H193 H 0 0 N N N 24.252 27.929 20.529 -0.774 2.125 1.058 H193 REA_v2 46
|
||||
REA_v2 H201 H201 H 0 0 N N N 24.620 23.168 25.369 5.026 0.871 1.762 H201 REA_v2 47
|
||||
REA_v2 H202 H202 H 0 0 N N N 24.965 23.516 23.641 5.707 1.771 0.386 H202 REA_v2 48
|
||||
REA_v2 H203 H203 H 0 0 N N N 24.360 24.822 24.717 3.952 1.795 0.685 H203 REA_v2 49
|
||||
REA_v2 HO2 HO2 H 0 1 N N N 22.244 21.180 27.713 9.006 -0.469 -0.490 HO2 REA_v2 50
|
||||
#
|
||||
loop_
|
||||
_chem_comp_bond.comp_id
|
||||
_chem_comp_bond.atom_id_1
|
||||
_chem_comp_bond.atom_id_2
|
||||
_chem_comp_bond.value_order
|
||||
_chem_comp_bond.pdbx_aromatic_flag
|
||||
_chem_comp_bond.pdbx_stereo_config
|
||||
_chem_comp_bond.pdbx_ordinal
|
||||
REA_v2 C1 C2 SING N N 1
|
||||
REA_v2 C1 C6 SING N N 2
|
||||
REA_v2 C1 C16 SING N N 3
|
||||
REA_v2 C1 C17 SING N N 4
|
||||
REA_v2 C2 C3 SING N N 5
|
||||
REA_v2 C2 H21 SING N N 6
|
||||
REA_v2 C2 H22 SING N N 7
|
||||
REA_v2 C3 C4 SING N N 8
|
||||
REA_v2 C3 H31 SING N N 9
|
||||
REA_v2 C3 H32 SING N N 10
|
||||
REA_v2 C4 C5 SING N N 11
|
||||
REA_v2 C4 H41 SING N N 12
|
||||
REA_v2 C4 H42 SING N N 13
|
||||
REA_v2 C5 C6 DOUB N N 14
|
||||
REA_v2 C5 C18 SING N N 15
|
||||
REA_v2 C6 C7 SING N N 16
|
||||
REA_v2 C7 C8 DOUB N E 17
|
||||
REA_v2 C7 H7 SING N N 18
|
||||
REA_v2 C8 C9 SING N N 19
|
||||
REA_v2 C8 H8 SING N N 20
|
||||
REA_v2 C9 C10 DOUB N E 21
|
||||
REA_v2 C9 C19 SING N N 22
|
||||
REA_v2 C10 C11 SING N N 23
|
||||
REA_v2 C10 H10 SING N N 24
|
||||
REA_v2 C11 C12 DOUB N E 25
|
||||
REA_v2 C11 H11 SING N N 26
|
||||
REA_v2 C12 C13 SING N N 27
|
||||
REA_v2 C12 H12 SING N N 28
|
||||
REA_v2 C13 C14 DOUB N E 29
|
||||
REA_v2 C13 C20 SING N N 30
|
||||
REA_v2 C14 C15 SING N N 31
|
||||
REA_v2 C14 H14 SING N N 32
|
||||
REA_v2 C15 O1 DOUB N N 33
|
||||
REA_v2 C15 O2 SING N N 34
|
||||
REA_v2 C16 H161 SING N N 35
|
||||
REA_v2 C16 H162 SING N N 36
|
||||
REA_v2 C16 H163 SING N N 37
|
||||
REA_v2 C17 H171 SING N N 38
|
||||
REA_v2 C17 H172 SING N N 39
|
||||
REA_v2 C17 H173 SING N N 40
|
||||
REA_v2 C18 H181 SING N N 41
|
||||
REA_v2 C18 H182 SING N N 42
|
||||
REA_v2 C18 H183 SING N N 43
|
||||
REA_v2 C19 H191 SING N N 44
|
||||
REA_v2 C19 H192 SING N N 45
|
||||
REA_v2 C19 H193 SING N N 46
|
||||
REA_v2 C20 H201 SING N N 47
|
||||
REA_v2 C20 H202 SING N N 48
|
||||
REA_v2 C20 H203 SING N N 49
|
||||
REA_v2 O2 HO2 SING N N 50
|
||||
#
|
||||
loop_
|
||||
_pdbx_chem_comp_descriptor.comp_id
|
||||
_pdbx_chem_comp_descriptor.type
|
||||
_pdbx_chem_comp_descriptor.program
|
||||
_pdbx_chem_comp_descriptor.program_version
|
||||
_pdbx_chem_comp_descriptor.descriptor
|
||||
REA_v2 SMILES ACDLabs 12.01 "C1(CCCC(=C1\C=C\C(=C\C=C\C(=C\C(=O)O)C)C)C)(C)C"
|
||||
REA_v2 InChI InChI 1.03 "InChI=1S/C20H28O2/c1-15(8-6-9-16(2)14-19(21)22)11-12-18-17(3)10-7-13-20(18,4)5/h6,8-9,11-12,14H,7,10,13H2,1-5H3,(H,21,22)/b9-6+,12-11+,15-8+,16-14+"
|
||||
REA_v2 InChIKey InChI 1.03 SHGAZHPCJJPHSC-YCNIQYBTSA-N
|
||||
REA_v2 SMILES_CANONICAL CACTVS 3.385 "CC1=C(\C=C\C(C)=C\C=C\C(C)=C\C(O)=O)C(C)(C)CCC1"
|
||||
REA_v2 SMILES CACTVS 3.385 "CC1=C(C=CC(C)=CC=CC(C)=CC(O)=O)C(C)(C)CCC1"
|
||||
REA_v2 SMILES_CANONICAL "OpenEye OEToolkits" 1.7.6 "CC1=C(C(CCC1)(C)C)/C=C/C(=C/C=C/C(=C/C(=O)O)/C)/C"
|
||||
REA_v2 SMILES "OpenEye OEToolkits" 1.7.6 "CC1=C(C(CCC1)(C)C)C=CC(=CC=CC(=CC(=O)O)C)C"
|
||||
#
|
||||
loop_
|
||||
_pdbx_chem_comp_identifier.comp_id
|
||||
_pdbx_chem_comp_identifier.type
|
||||
_pdbx_chem_comp_identifier.program
|
||||
_pdbx_chem_comp_identifier.program_version
|
||||
_pdbx_chem_comp_identifier.identifier
|
||||
REA_v2 "SYSTEMATIC NAME" ACDLabs 12.01 "retinoic acid"
|
||||
REA_v2 "SYSTEMATIC NAME" "OpenEye OEToolkits" 1.7.6 "(2E,4E,6E,8E)-3,7-dimethyl-9-(2,6,6-trimethylcyclohexen-1-yl)nona-2,4,6,8-tetraenoic acid"
|
||||
#
|
||||
loop_
|
||||
_pdbx_chem_comp_audit.comp_id
|
||||
_pdbx_chem_comp_audit.action_type
|
||||
_pdbx_chem_comp_audit.date
|
||||
_pdbx_chem_comp_audit.processing_site
|
||||
REA_v2 "CREA_v2te component" 1999-07-08 RCSB
|
||||
REA_v2 "Modify descriptor" 2011-06-04 RCSB
|
||||
REA_v2 "Other modification" 2016-10-18 RCSB
|
||||
#
|
||||
39
test/io-test.cpp
Normal file
39
test/io-test.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <cif++.hpp>
|
||||
|
||||
class dummy_parser : public cif::sac_parser
|
||||
{
|
||||
public:
|
||||
dummy_parser(std::istream &is)
|
||||
: sac_parser(is)
|
||||
{
|
||||
}
|
||||
|
||||
void produce_datablock(std::string_view name) override
|
||||
{
|
||||
}
|
||||
|
||||
void produce_category(std::string_view name) override
|
||||
{
|
||||
}
|
||||
|
||||
void produce_row() override
|
||||
{
|
||||
}
|
||||
|
||||
void produce_item(std::string_view category, std::string_view item, std::string_view value) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
cif::gzio::ifstream in("/srv/data/pdb/mmCIF/gl/8glv.cif.gz");
|
||||
|
||||
dummy_parser parser(in);
|
||||
parser.parse_file();
|
||||
|
||||
// cif::file f("/srv/data/pdb/mmCIF/gl/8glv.cif.gz");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -75,6 +75,30 @@ bool init_unit_test()
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
BOOST_AUTO_TEST_CASE(id_1)
|
||||
{
|
||||
BOOST_TEST(cif::cif_id_for_number(0) == "A");
|
||||
BOOST_TEST(cif::cif_id_for_number(25) == "Z");
|
||||
BOOST_TEST(cif::cif_id_for_number(26) == "AA");
|
||||
BOOST_TEST(cif::cif_id_for_number(26 + 1) == "AB");
|
||||
|
||||
BOOST_TEST(cif::cif_id_for_number(26 + 26 * 26 - 1) == "ZZ");
|
||||
BOOST_TEST(cif::cif_id_for_number(26 + 26 * 26) == "AAA");
|
||||
BOOST_TEST(cif::cif_id_for_number(26 + 26 * 26 + 1) == "AAB");
|
||||
|
||||
std::set<std::string> testset;
|
||||
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
{
|
||||
std::string id = cif::cif_id_for_number(i);
|
||||
BOOST_TEST(testset.count(id) == 0);
|
||||
testset.insert(id);
|
||||
}
|
||||
BOOST_TEST(testset.size() == 100000);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cc_1)
|
||||
{
|
||||
std::tuple<std::string_view, float, char> tests[] = {
|
||||
@@ -2357,8 +2381,6 @@ _test.text ??
|
||||
|
||||
BOOST_AUTO_TEST_CASE(output_test_1)
|
||||
{
|
||||
cif::VERBOSE = 5;
|
||||
|
||||
auto data1 = R"(
|
||||
data_Q
|
||||
loop_
|
||||
@@ -2863,7 +2885,7 @@ save__cat_1.name
|
||||
|
||||
std::istream is_dict(&buffer);
|
||||
|
||||
auto validator = cif::parse_dictionary("test_dict.dic", is_dict);
|
||||
auto &validator = cif::validator_factory::instance().construct_validator("test_dict.dic", is_dict);
|
||||
|
||||
cif::file f;
|
||||
f.set_validator(&validator);
|
||||
@@ -2901,8 +2923,6 @@ _cat_1.name
|
||||
ss << f;
|
||||
|
||||
cif::file f2(ss);
|
||||
|
||||
f2.set_validator(&validator);
|
||||
BOOST_ASSERT(f2.is_valid());
|
||||
|
||||
auto &audit_conform = f2.front()["audit_conform"];
|
||||
@@ -3130,4 +3150,60 @@ _test.value
|
||||
|
||||
v = test.find1<std::optional<float>>("id"_key == 4, "value");
|
||||
BOOST_CHECK(v.has_value() == false);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
BOOST_AUTO_TEST_CASE(compound_test_1)
|
||||
{
|
||||
cif::compound_factory::instance().push_dictionary(gTestDir / "REA_v2.cif");
|
||||
auto compound = cif::compound_factory::instance().create("REA_v2");
|
||||
BOOST_ASSERT(compound != nullptr);
|
||||
BOOST_CHECK(compound->id() == "REA_v2");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pdb_parser_test_1)
|
||||
{
|
||||
char k1CBS[] = R"(HEADER RETINOIC-ACID TRANSPORT 28-SEP-94 1CBS
|
||||
TITLE CRYSTAL STRUCTURE OF CELLULAR RETINOIC-ACID-BINDING
|
||||
TITLE 2 PROTEINS I AND II IN COMPLEX WITH ALL-TRANS-RETINOIC ACID
|
||||
TITLE 3 AND A SYNTHETIC RETINOID
|
||||
COMPND MOL_ID: 1;
|
||||
COMPND 2 MOLECULE: CELLULAR RETINOIC ACID BINDING PROTEIN TYPE II;
|
||||
COMPND 3 CHAIN: A;
|
||||
COMPND 4 ENGINEERED: YES
|
||||
SOURCE MOL_ID: 1;
|
||||
SOURCE 2 ORGANISM_SCIENTIFIC: HOMO SAPIENS;
|
||||
SOURCE 3 ORGANISM_COMMON: HUMAN;
|
||||
SOURCE 4 ORGANISM_TAXID: 9606;
|
||||
SOURCE 5 CELL_LINE: BL21;
|
||||
SOURCE 6 GENE: HUMAN CRABP-II;
|
||||
SOURCE 7 EXPRESSION_SYSTEM: ESCHERICHIA COLI BL21(DE3);
|
||||
SOURCE 8 EXPRESSION_SYSTEM_TAXID: 469008;
|
||||
SOURCE 9 EXPRESSION_SYSTEM_STRAIN: BL21 (DE3);
|
||||
SOURCE 10 EXPRESSION_SYSTEM_PLASMID: PET-3A
|
||||
KEYWDS RETINOIC-ACID TRANSPORT
|
||||
EXPDTA X-RAY DIFFRACTION
|
||||
AUTHOR G.J.KLEYWEGT,T.BERGFORS,T.A.JONES
|
||||
ATOM 1 N PRO A 1 16.979 13.301 44.555 1.00 30.05 N
|
||||
ATOM 2 CA PRO A 1 18.150 13.525 43.680 1.00 28.82 C
|
||||
ATOM 3 C PRO A 1 18.656 14.966 43.784 1.00 26.59 C
|
||||
ATOM 4 O PRO A 1 17.890 15.889 44.078 1.00 26.84 O
|
||||
ATOM 5 CB PRO A 1 17.678 13.270 42.255 1.00 29.24 C
|
||||
ATOM 6 CG PRO A 1 16.248 13.734 42.347 1.00 29.29 C
|
||||
ATOM 7 CD PRO A 1 15.762 13.216 43.724 1.00 30.71 C)";
|
||||
|
||||
struct membuf : public std::streambuf
|
||||
{
|
||||
membuf(char *text, size_t length)
|
||||
{
|
||||
this->setg(text, text, text + length);
|
||||
}
|
||||
} buffer(k1CBS, sizeof(k1CBS) - 1);
|
||||
|
||||
std::istream is(&buffer);
|
||||
|
||||
auto f = cif::pdb::read(is);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user