Mercurial > hg > orthanc
changeset 3593:0301f59450fe
improved the patch for loading DICOM dictionary from memory
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 06 Jan 2020 17:58:15 +0100 |
parents | de1665e7b341 |
children | 0654d0838de8 |
files | Resources/CMake/DcmtkConfigurationStatic-3.6.2.cmake Resources/CMake/DcmtkConfigurationStatic-3.6.4.cmake Resources/Patches/dcmtk-3.6.2-cmath.patch Resources/Patches/dcmtk-3.6.2.patch Resources/Patches/dcmtk-3.6.4.patch Resources/Patches/dcmtk-dcdict_orthanc.cc Resources/WebAssembly/dcdict.cc Resources/WebAssembly/dcdict.h |
diffstat | 8 files changed, 319 insertions(+), 1460 deletions(-) [+] |
line wrap: on
line diff
--- a/Resources/CMake/DcmtkConfigurationStatic-3.6.2.cmake Mon Dec 23 15:46:50 2019 +0100 +++ b/Resources/CMake/DcmtkConfigurationStatic-3.6.2.cmake Mon Jan 06 17:58:15 2020 +0100 @@ -31,7 +31,7 @@ message("Applying patch to detect mathematic primitives in DCMTK 3.6.2 with C++11") execute_process( COMMAND ${PATCH_EXECUTABLE} -p0 -N -i - ${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.2-cmath.patch + ${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.2.patch WORKING_DIRECTORY ${CMAKE_BINARY_DIR} RESULT_VARIABLE Failure ) @@ -39,6 +39,11 @@ if (Failure) message(FATAL_ERROR "Error while patching a file") endif() + + configure_file( + ${ORTHANC_ROOT}/Resources/Patches/dcmtk-dcdict_orthanc.cc + ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/dcdict_orthanc.cc + COPYONLY) else() message("The patches for DCMTK have already been applied") endif() @@ -72,18 +77,21 @@ SET(DCMTK_ENABLE_CHARSET_CONVERSION "iconv" CACHE STRING "") SET(HAVE_SYS_GETTID 0 CACHE INTERNAL "") - execute_process( - COMMAND ${PATCH_EXECUTABLE} -p0 -N -i - ${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.2-linux-standard-base.patch - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - RESULT_VARIABLE Failure - ) + if (FirstRun) + execute_process( + COMMAND ${PATCH_EXECUTABLE} -p0 -N -i + ${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.2-linux-standard-base.patch + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE Failure + ) - if (FirstRun AND Failure) - message(FATAL_ERROR "Error while patching a file") + if (Failure) + message(FATAL_ERROR "Error while patching a file") + endif() endif() endif() + SET(DCMTK_SOURCE_DIR ${DCMTK_SOURCES_DIR}) include(${DCMTK_SOURCES_DIR}/CMake/CheckFunctionWithHeaderExists.cmake) include(${DCMTK_SOURCES_DIR}/CMake/GenerateDCMTKConfigure.cmake) @@ -163,19 +171,6 @@ endif() -if (ORTHANC_SANDBOXED) - configure_file( - ${ORTHANC_ROOT}/Resources/WebAssembly/dcdict.h - ${DCMTK_SOURCES_DIR}/dcmdata/include/dcmtk/dcmdata/dcdict.h - COPYONLY) - - configure_file( - ${ORTHANC_ROOT}/Resources/WebAssembly/dcdict.cc - ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/dcdict.cc - COPYONLY) -endif() - - #set_source_files_properties(${DCMTK_SOURCES} # PROPERTIES COMPILE_DEFINITIONS # "PACKAGE_VERSION=\"${DCMTK_PACKAGE_VERSION}\";PACKAGE_VERSION_NUMBER=\"${DCMTK_VERSION_NUMBER}\"")
--- a/Resources/CMake/DcmtkConfigurationStatic-3.6.4.cmake Mon Dec 23 15:46:50 2019 +0100 +++ b/Resources/CMake/DcmtkConfigurationStatic-3.6.4.cmake Mon Jan 06 17:58:15 2020 +0100 @@ -25,16 +25,25 @@ DownloadPackage(${DCMTK_MD5} ${DCMTK_URL} "${DCMTK_SOURCES_DIR}") -# Apply the patches -execute_process( - COMMAND ${PATCH_EXECUTABLE} -p0 -N -i - ${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.4.patch - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - RESULT_VARIABLE Failure - ) +if (FirstRun) + # Apply the patches + execute_process( + COMMAND ${PATCH_EXECUTABLE} -p0 -N -i + ${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.4.patch + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE Failure + ) -if (FirstRun AND Failure) - message(FATAL_ERROR "Error while patching files") + if (Failure) + message(FATAL_ERROR "Error while patching a file") + endif() + + configure_file( + ${ORTHANC_ROOT}/Resources/Patches/dcmtk-dcdict_orthanc.cc + ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/dcdict_orthanc.cc + COPYONLY) +else() + message("The patches for DCMTK have already been applied") endif() @@ -152,22 +161,10 @@ endif() -if (ORTHANC_SANDBOXED) - configure_file( - ${ORTHANC_ROOT}/Resources/WebAssembly/dcdict.h - ${DCMTK_SOURCES_DIR}/dcmdata/include/dcmtk/dcmdata/dcdict.h - COPYONLY) - - configure_file( - ${ORTHANC_ROOT}/Resources/WebAssembly/dcdict.cc - ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/dcdict.cc - COPYONLY) -endif() - - list(REMOVE_ITEM DCMTK_SOURCES ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/mkdictbi.cc ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/mkdeftag.cc + ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/dcdict_orthanc.cc )
--- a/Resources/Patches/dcmtk-3.6.2-cmath.patch Mon Dec 23 15:46:50 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -diff -urEb dcmtk-3.6.2.orig/CMake/GenerateDCMTKConfigure.cmake dcmtk-3.6.2/CMake/GenerateDCMTKConfigure.cmake ---- dcmtk-3.6.2.orig/CMake/GenerateDCMTKConfigure.cmake 2018-09-20 09:30:34.364831213 +0200 -+++ dcmtk-3.6.2/CMake/GenerateDCMTKConfigure.cmake 2018-09-20 09:47:52.013660067 +0200 -@@ -568,12 +568,12 @@ - ENDIF(HAVE_CSTDDEF) - - CHECK_FUNCTIONWITHHEADER_EXISTS(feenableexcept "${HEADERS}" HAVE_PROTOTYPE_FEENABLEEXCEPT) -- CHECK_FUNCTIONWITHHEADER_EXISTS(isinf "${HEADERS}" HAVE_PROTOTYPE_ISINF) -- CHECK_FUNCTIONWITHHEADER_EXISTS(isnan "${HEADERS}" HAVE_PROTOTYPE_ISNAN) -- CHECK_FUNCTIONWITHHEADER_EXISTS(finite "${HEADERS}" HAVE_PROTOTYPE_FINITE) -- CHECK_FUNCTIONWITHHEADER_EXISTS(std::isinf "${HEADERS}" HAVE_PROTOTYPE_STD__ISINF) -- CHECK_FUNCTIONWITHHEADER_EXISTS(std::isnan "${HEADERS}" HAVE_PROTOTYPE_STD__ISNAN) -- CHECK_FUNCTIONWITHHEADER_EXISTS(std::finite "${HEADERS}" HAVE_PROTOTYPE_STD__FINITE) -+ CHECK_FUNCTIONWITHHEADER_EXISTS("isinf(0.)" "${HEADERS}" HAVE_PROTOTYPE_ISINF) -+ CHECK_FUNCTIONWITHHEADER_EXISTS("isnan(0.)" "${HEADERS}" HAVE_PROTOTYPE_ISNAN) -+ CHECK_FUNCTIONWITHHEADER_EXISTS("finite(0.)" "${HEADERS}" HAVE_PROTOTYPE_FINITE) -+ CHECK_FUNCTIONWITHHEADER_EXISTS("std::isinf(0.)" "${HEADERS}" HAVE_PROTOTYPE_STD__ISINF) -+ CHECK_FUNCTIONWITHHEADER_EXISTS("std::isnan(0.)" "${HEADERS}" HAVE_PROTOTYPE_STD__ISNAN) -+ CHECK_FUNCTIONWITHHEADER_EXISTS("std::finite(0.)" "${HEADERS}" HAVE_PROTOTYPE_STD__FINITE) - CHECK_FUNCTIONWITHHEADER_EXISTS(flock "${HEADERS}" HAVE_PROTOTYPE_FLOCK) - CHECK_FUNCTIONWITHHEADER_EXISTS(gethostbyname "${HEADERS}" HAVE_PROTOTYPE_GETHOSTBYNAME) - CHECK_FUNCTIONWITHHEADER_EXISTS(gethostbyname_r "${HEADERS}" HAVE_PROTOTYPE_GETHOSTBYNAME_R)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Patches/dcmtk-3.6.2.patch Mon Jan 06 17:58:15 2020 +0100 @@ -0,0 +1,48 @@ +diff -urEb dcmtk-3.6.2.orig/CMake/GenerateDCMTKConfigure.cmake dcmtk-3.6.2/CMake/GenerateDCMTKConfigure.cmake +--- dcmtk-3.6.2.orig/CMake/GenerateDCMTKConfigure.cmake 2020-01-06 17:42:52.299540389 +0100 ++++ dcmtk-3.6.2/CMake/GenerateDCMTKConfigure.cmake 2020-01-06 17:43:56.707520036 +0100 +@@ -568,12 +568,12 @@ + ENDIF(HAVE_CSTDDEF) + + CHECK_FUNCTIONWITHHEADER_EXISTS(feenableexcept "${HEADERS}" HAVE_PROTOTYPE_FEENABLEEXCEPT) +- CHECK_FUNCTIONWITHHEADER_EXISTS(isinf "${HEADERS}" HAVE_PROTOTYPE_ISINF) +- CHECK_FUNCTIONWITHHEADER_EXISTS(isnan "${HEADERS}" HAVE_PROTOTYPE_ISNAN) +- CHECK_FUNCTIONWITHHEADER_EXISTS(finite "${HEADERS}" HAVE_PROTOTYPE_FINITE) +- CHECK_FUNCTIONWITHHEADER_EXISTS(std::isinf "${HEADERS}" HAVE_PROTOTYPE_STD__ISINF) +- CHECK_FUNCTIONWITHHEADER_EXISTS(std::isnan "${HEADERS}" HAVE_PROTOTYPE_STD__ISNAN) +- CHECK_FUNCTIONWITHHEADER_EXISTS(std::finite "${HEADERS}" HAVE_PROTOTYPE_STD__FINITE) ++ CHECK_FUNCTIONWITHHEADER_EXISTS("isinf(0.)" "${HEADERS}" HAVE_PROTOTYPE_ISINF) ++ CHECK_FUNCTIONWITHHEADER_EXISTS("isnan(0.)" "${HEADERS}" HAVE_PROTOTYPE_ISNAN) ++ CHECK_FUNCTIONWITHHEADER_EXISTS("finite(0.)" "${HEADERS}" HAVE_PROTOTYPE_FINITE) ++ CHECK_FUNCTIONWITHHEADER_EXISTS("std::isinf(0.)" "${HEADERS}" HAVE_PROTOTYPE_STD__ISINF) ++ CHECK_FUNCTIONWITHHEADER_EXISTS("std::isnan(0.)" "${HEADERS}" HAVE_PROTOTYPE_STD__ISNAN) ++ CHECK_FUNCTIONWITHHEADER_EXISTS("std::finite(0.)" "${HEADERS}" HAVE_PROTOTYPE_STD__FINITE) + CHECK_FUNCTIONWITHHEADER_EXISTS(flock "${HEADERS}" HAVE_PROTOTYPE_FLOCK) + CHECK_FUNCTIONWITHHEADER_EXISTS(gethostbyname "${HEADERS}" HAVE_PROTOTYPE_GETHOSTBYNAME) + CHECK_FUNCTIONWITHHEADER_EXISTS(gethostbyname_r "${HEADERS}" HAVE_PROTOTYPE_GETHOSTBYNAME_R) +diff -urEb dcmtk-3.6.2.orig/dcmdata/include/dcmtk/dcmdata/dcdict.h dcmtk-3.6.2/dcmdata/include/dcmtk/dcmdata/dcdict.h +--- dcmtk-3.6.2.orig/dcmdata/include/dcmtk/dcmdata/dcdict.h 2020-01-06 17:42:52.283540394 +0100 ++++ dcmtk-3.6.2/dcmdata/include/dcmtk/dcmdata/dcdict.h 2020-01-06 17:46:21.711473976 +0100 +@@ -152,6 +152,12 @@ + /// returns an iterator to the end of the repeating tag dictionary + DcmDictEntryListIterator repeatingEnd() { return repDict.end(); } + ++ // Function by the Orthanc project to load a dictionary from a ++ // memory buffer, which is necessary in sandboxed ++ // environments. This is an adapted version of ++ // DcmDataDictionary::loadDictionary(). ++ OFBool loadFromMemory(const std::string& content, OFBool errorIfAbsent = OFTrue); ++ + private: + + /** private undefined assignment operator +diff -urEb dcmtk-3.6.2.orig/dcmdata/libsrc/dcdict.cc dcmtk-3.6.2/dcmdata/libsrc/dcdict.cc +--- dcmtk-3.6.2.orig/dcmdata/libsrc/dcdict.cc 2020-01-06 17:42:52.287540392 +0100 ++++ dcmtk-3.6.2/dcmdata/libsrc/dcdict.cc 2020-01-06 17:47:18.335299472 +0100 +@@ -876,3 +876,6 @@ + wrlock().clear(); + unlock(); + } ++ ++ ++#include "dcdict_orthanc.cc"
--- a/Resources/Patches/dcmtk-3.6.4.patch Mon Dec 23 15:46:50 2019 +0100 +++ b/Resources/Patches/dcmtk-3.6.4.patch Mon Jan 06 17:58:15 2020 +0100 @@ -1,6 +1,32 @@ +diff -urEb dcmtk-3.6.4.orig/dcmdata/include/dcmtk/dcmdata/dcdict.h dcmtk-3.6.4/dcmdata/include/dcmtk/dcmdata/dcdict.h +--- dcmtk-3.6.4.orig/dcmdata/include/dcmtk/dcmdata/dcdict.h 2020-01-06 17:38:11.323628123 +0100 ++++ dcmtk-3.6.4/dcmdata/include/dcmtk/dcmdata/dcdict.h 2020-01-06 17:39:29.519603910 +0100 +@@ -152,6 +152,12 @@ + /// returns an iterator to the end of the repeating tag dictionary + DcmDictEntryListIterator repeatingEnd() { return repDict.end(); } + ++ // Function by the Orthanc project to load a dictionary from a ++ // memory buffer, which is necessary in sandboxed ++ // environments. This is an adapted version of ++ // DcmDataDictionary::loadDictionary(). ++ OFBool loadFromMemory(const std::string& content, OFBool errorIfAbsent = OFTrue); ++ + private: + + /** private undefined assignment operator +diff -urEb dcmtk-3.6.4.orig/dcmdata/libsrc/dcdict.cc dcmtk-3.6.4/dcmdata/libsrc/dcdict.cc +--- dcmtk-3.6.4.orig/dcmdata/libsrc/dcdict.cc 2020-01-06 17:38:11.327628121 +0100 ++++ dcmtk-3.6.4/dcmdata/libsrc/dcdict.cc 2020-01-06 17:39:58.927594760 +0100 +@@ -899,3 +899,6 @@ + wrlock().clear(); + wrunlock(); + } ++ ++ ++#include "dcdict_orthanc.cc" diff -urEb dcmtk-3.6.4.orig/dcmdata/libsrc/dcpxitem.cc dcmtk-3.6.4/dcmdata/libsrc/dcpxitem.cc ---- dcmtk-3.6.4.orig/dcmdata/libsrc/dcpxitem.cc 2019-02-21 15:30:21.657110805 +0100 -+++ dcmtk-3.6.4/dcmdata/libsrc/dcpxitem.cc 2019-02-21 16:28:43.721049550 +0100 +--- dcmtk-3.6.4.orig/dcmdata/libsrc/dcpxitem.cc 2020-01-06 17:38:11.327628121 +0100 ++++ dcmtk-3.6.4/dcmdata/libsrc/dcpxitem.cc 2020-01-06 17:38:14.195627236 +0100 @@ -36,6 +36,9 @@ #include "dcmtk/dcmdata/dcostrma.h" /* for class DcmOutputStream */ #include "dcmtk/dcmdata/dcwcache.h" /* for class DcmWriteCache */ @@ -12,8 +38,8 @@ // ******************************** diff -urEb dcmtk-3.6.4.orig/ofstd/include/dcmtk/ofstd/offile.h dcmtk-3.6.4/ofstd/include/dcmtk/ofstd/offile.h ---- dcmtk-3.6.4.orig/ofstd/include/dcmtk/ofstd/offile.h 2019-02-21 15:30:21.645110805 +0100 -+++ dcmtk-3.6.4/ofstd/include/dcmtk/ofstd/offile.h 2019-02-21 15:30:48.273110339 +0100 +--- dcmtk-3.6.4.orig/ofstd/include/dcmtk/ofstd/offile.h 2020-01-06 17:38:11.371628108 +0100 ++++ dcmtk-3.6.4/ofstd/include/dcmtk/ofstd/offile.h 2020-01-06 17:38:14.195627236 +0100 @@ -575,7 +575,7 @@ */ void setlinebuf() @@ -23,4 +49,3 @@ this->setvbuf(NULL, _IOLBF, 0); #else :: setlinebuf(file_); -Only in dcmtk-3.6.4/ofstd/include/dcmtk/ofstd: offile.h~
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Patches/dcmtk-dcdict_orthanc.cc Mon Jan 06 17:58:15 2020 +0100 @@ -0,0 +1,205 @@ +// Function by the Orthanc project to load a dictionary from a memory +// buffer, which is necessary in sandboxed environments. This is an +// adapted version of DcmDataDictionary::loadDictionary(). + + +#include <boost/noncopyable.hpp> + +struct OrthancLinesIterator; + +// This plain old C class is implemented in "../../Core/Toolbox.h" +OrthancLinesIterator* OrthancLinesIterator_Create(const std::string& content); + +bool OrthancLinesIterator_GetLine(std::string& target, + const OrthancLinesIterator* iterator); + +void OrthancLinesIterator_Next(OrthancLinesIterator* iterator); + +void OrthancLinesIterator_Free(OrthancLinesIterator* iterator); + + +class LinesIterator : public boost::noncopyable +{ +private: + OrthancLinesIterator* iterator_; + +public: + LinesIterator(const std::string& content) : + iterator_(NULL) + { + iterator_ = OrthancLinesIterator_Create(content); + } + + ~LinesIterator() + { + if (iterator_ != NULL) + { + OrthancLinesIterator_Free(iterator_); + iterator_ = NULL; + } + } + + bool GetLine(std::string& target) const + { + if (iterator_ != NULL) + { + return OrthancLinesIterator_GetLine(target, iterator_); + } + else + { + return false; + } + } + + void Next() + { + if (iterator_ != NULL) + { + OrthancLinesIterator_Next(iterator_); + } + } +}; + + + +OFBool +DcmDataDictionary::loadFromMemory(const std::string& content, OFBool errorIfAbsent) +{ + int lineNumber = 0; + char* lineFields[DCM_MAXDICTFIELDS + 1]; + int fieldsPresent; + DcmDictEntry* e; + int errorsEncountered = 0; + OFBool errorOnThisLine = OFFalse; + int i; + + DcmTagKey key, upperKey; + DcmDictRangeRestriction groupRestriction = DcmDictRange_Unspecified; + DcmDictRangeRestriction elementRestriction = DcmDictRange_Unspecified; + DcmVR vr; + char* vrName; + char* tagName; + char* privCreator; + int vmMin, vmMax = 1; + const char* standardVersion; + + LinesIterator iterator(content); + + std::string line; + while (iterator.GetLine(line)) { + iterator.Next(); + + if (line.size() >= DCM_MAXDICTLINESIZE) { + DCMDATA_ERROR("DcmDataDictionary: Too long line: " << line); + continue; + } + + lineNumber++; + + if (onlyWhitespace(line.c_str())) { + continue; /* ignore this line */ + } + if (isaCommentLine(line.c_str())) { + continue; /* ignore this line */ + } + + errorOnThisLine = OFFalse; + + /* fields are tab separated */ + fieldsPresent = splitFields(line.c_str(), lineFields, + DCM_MAXDICTFIELDS, + DCM_DICT_FIELD_SEPARATOR_CHAR); + + /* initialize dict entry fields */ + vrName = NULL; + tagName = NULL; + privCreator = NULL; + vmMin = vmMax = 1; + standardVersion = "DICOM"; + + switch (fieldsPresent) { + case 0: + case 1: + case 2: + DCMDATA_ERROR("DcmDataDictionary: " + << "too few fields (line " << lineNumber << ")"); + errorOnThisLine = OFTrue; + break; + default: + DCMDATA_ERROR("DcmDataDictionary: " + << "too many fields (line " << lineNumber << "): "); + errorOnThisLine = OFTrue; + break; + case 5: + stripWhitespace(lineFields[4]); + standardVersion = lineFields[4]; + /* drop through to next case label */ + case 4: + /* the VM field is present */ + if (!parseVMField(lineFields[3], vmMin, vmMax)) { + DCMDATA_ERROR("DcmDataDictionary: " + << "bad VM field (line " << lineNumber << "): " << lineFields[3]); + errorOnThisLine = OFTrue; + } + /* drop through to next case label */ + case 3: + if (!parseWholeTagField(lineFields[0], key, upperKey, + groupRestriction, elementRestriction, privCreator)) + { + DCMDATA_ERROR("DcmDataDictionary: " + << "bad Tag field (line " << lineNumber << "): " << lineFields[0]); + errorOnThisLine = OFTrue; + } else { + /* all is OK */ + vrName = lineFields[1]; + stripWhitespace(vrName); + + tagName = lineFields[2]; + stripWhitespace(tagName); + } + } + + if (!errorOnThisLine) { + /* check the VR Field */ + vr.setVR(vrName); + if (vr.getEVR() == EVR_UNKNOWN) { + DCMDATA_ERROR("DcmDataDictionary: " + << "bad VR field (line " << lineNumber << "): " << vrName); + errorOnThisLine = OFTrue; + } + } + + if (!errorOnThisLine) { + e = new DcmDictEntry( + key.getGroup(), key.getElement(), + upperKey.getGroup(), upperKey.getElement(), + vr, tagName, vmMin, vmMax, standardVersion, OFTrue, + privCreator); + + e->setGroupRangeRestriction(groupRestriction); + e->setElementRangeRestriction(elementRestriction); + addEntry(e); + } + + for (i = 0; i < fieldsPresent; i++) { + free(lineFields[i]); + lineFields[i] = NULL; + } + + delete[] privCreator; + + if (errorOnThisLine) { + errorsEncountered++; + } + } + + /* return OFFalse in case of errors and set internal state accordingly */ + if (errorsEncountered == 0) { + dictionaryLoaded = OFTrue; + return OFTrue; + } + else { + dictionaryLoaded = OFFalse; + return OFFalse; + } +}
--- a/Resources/WebAssembly/dcdict.cc Mon Dec 23 15:46:50 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1087 +0,0 @@ -/* - * - * Copyright (C) 1994-2016, OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were developed by - * - * OFFIS e.V. - * R&D Division Health - * Escherweg 2 - * D-26121 Oldenburg, Germany - * - * - * Module: dcmdata - * - * Author: Andrew Hewett - * - * Purpose: loadable DICOM data dictionary - * - */ - - -#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ - -#include "dcmtk/ofstd/ofstd.h" -#include "dcmtk/dcmdata/dcdict.h" -#include "dcmtk/ofstd/ofdefine.h" -#include "dcmtk/dcmdata/dcdicent.h" -#include "dcmtk/dcmdata/dctypes.h" - -#define INCLUDE_CSTDLIB -#define INCLUDE_CSTDIO -#define INCLUDE_CSTRING -#define INCLUDE_CCTYPE -#include "dcmtk/ofstd/ofstdinc.h" - -/* -** The separator character between fields in the data dictionary file(s) -*/ -#define DCM_DICT_FIELD_SEPARATOR_CHAR '\t' - -/* -** Comment character for the data dictionary file(s) -*/ -#define DCM_DICT_COMMENT_CHAR '#' - -/* -** THE Global DICOM Data Dictionary -*/ - -GlobalDcmDataDictionary dcmDataDict; - - -/* -** Member Functions -*/ - -static DcmDictEntry* -makeSkelEntry(Uint16 group, Uint16 element, - Uint16 upperGroup, Uint16 upperElement, - DcmEVR evr, const char* tagName, int vmMin, int vmMax, - const char* standardVersion, - DcmDictRangeRestriction groupRestriction, - DcmDictRangeRestriction elementRestriction, - const char* privCreator) -{ - DcmDictEntry* e = NULL; - e = new DcmDictEntry(group, element, upperGroup, upperElement, evr, - tagName, vmMin, vmMax, standardVersion, OFFalse, privCreator); - if (e != NULL) { - e->setGroupRangeRestriction(groupRestriction); - e->setElementRangeRestriction(elementRestriction); - } - return e; -} - - -OFBool DcmDataDictionary::loadSkeletonDictionary() -{ - /* - ** We need to know about Group Lengths to compute them - */ - DcmDictEntry* e = NULL; - e = makeSkelEntry(0x0000, 0x0000, 0xffff, 0x0000, - EVR_UL, "GenericGroupLength", 1, 1, "GENERIC", - DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL); - addEntry(e); - - /* - ** We need to know about Items and Delimitation Items to parse - ** (and construct) sequences. - */ - e = makeSkelEntry(0xfffe, 0xe000, 0xfffe, 0xe000, - EVR_na, "Item", 1, 1, "DICOM", - DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL); - addEntry(e); - e = makeSkelEntry(0xfffe, 0xe00d, 0xfffe, 0xe00d, - EVR_na, "ItemDelimitationItem", 1, 1, "DICOM", - DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL); - addEntry(e); - e = makeSkelEntry(0xfffe, 0xe0dd, 0xfffe, 0xe0dd, - EVR_na, "SequenceDelimitationItem", 1, 1, "DICOM", - DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL); - addEntry(e); - - skeletonCount = numberOfEntries(); - return OFTrue; -} - - -DcmDataDictionary::DcmDataDictionary(OFBool loadBuiltin, OFBool loadExternal) - : hashDict(), - repDict(), - skeletonCount(0), - dictionaryLoaded(OFFalse) -{ - reloadDictionaries(loadBuiltin, loadExternal); -} - -DcmDataDictionary::~DcmDataDictionary() -{ - clear(); -} - - -void DcmDataDictionary::clear() -{ - hashDict.clear(); - repDict.clear(); - skeletonCount = 0; - dictionaryLoaded = OFFalse; -} - - -static void -stripWhitespace(char* s) -{ - if (s) - { - unsigned char c; - unsigned char *t; - unsigned char *p; - t=p=OFreinterpret_cast(unsigned char *, s); - while ((c = *t++)) if (!isspace(c)) *p++ = c; - *p = '\0'; - } -} - -static char* -stripTrailingWhitespace(char* s) -{ - if (s == NULL) return s; - for - ( - char* it = s + strlen(s) - 1; - it >= s && isspace(OFstatic_cast(unsigned char, *it)); - *it-- = '\0' - ); - return s; -} - -static void -stripLeadingWhitespace(char* s) -{ - if (s) - { - unsigned char c; - unsigned char *t; - unsigned char *p; - t=p=OFreinterpret_cast(unsigned char *, s); - while (isspace(*t)) t++; - while ((c = *t++)) *p++ = c; - *p = '\0'; - } -} - -static OFBool -parseVMField(char* vmField, int& vmMin, int& vmMax) -{ - OFBool ok = OFTrue; - char c = 0; - int dummy = 0; - - /* strip any whitespace */ - stripWhitespace(vmField); - - if (sscanf(vmField, "%d-%d%c", &vmMin, &dummy, &c) == 3) { - /* treat "2-2n" like "2-n" for the moment */ - if ((c == 'n') || (c == 'N')) { - vmMax = DcmVariableVM; - } else { - ok = OFFalse; - } - } else if (sscanf(vmField, "%d-%d", &vmMin, &vmMax) == 2) { - /* range VM (e.g. "2-6") */ - } else if (sscanf(vmField, "%d-%c", &vmMin, &c) == 2) { - if ((c == 'n') || (c == 'N')) { - vmMax = DcmVariableVM; - } else { - ok = OFFalse; - } - } else if (sscanf(vmField, "%d%c", &vmMin, &c) == 2) { - /* treat "2n" like "2-n" for the moment */ - if ((c == 'n') || (c == 'N')) { - vmMax = DcmVariableVM; - } else { - ok = OFFalse; - } - } else if (sscanf(vmField, "%d", &vmMin) == 1) { - /* fixed VM */ - vmMax = vmMin; - } else if (sscanf(vmField, "%c", &c) == 1) { - /* treat "n" like "1-n" */ - if ((c == 'n') || (c == 'N')) { - vmMin = 1; - vmMax = DcmVariableVM; - } else { - ok = OFFalse; - } - } else { - ok = OFFalse; - } - return ok; -} - -static int -splitFields(const char* line, char* fields[], int maxFields, char splitChar) -{ - const char *p; - int foundFields = 0; - size_t len; - - do { -#ifdef __BORLANDC__ - // Borland Builder expects a non-const argument - p = strchr(OFconst_cast(char *, line), splitChar); -#else - p = strchr(line, splitChar); -#endif - if (p == NULL) { - len = strlen(line); - } else { - len = p - line; - } - fields[foundFields] = OFstatic_cast(char *, malloc(len + 1)); - strncpy(fields[foundFields], line, len); - fields[foundFields][len] = '\0'; - foundFields++; - line = p + 1; - } while ((foundFields < maxFields) && (p != NULL)); - - return foundFields; -} - -static OFBool -parseTagPart(char *s, unsigned int& l, unsigned int& h, - DcmDictRangeRestriction& r) -{ - OFBool ok = OFTrue; - char restrictor = ' '; - - r = DcmDictRange_Unspecified; /* by default */ - - if (sscanf(s, "%x-%c-%x", &l, &restrictor, &h) == 3) { - switch (restrictor) { - case 'o': - case 'O': - r = DcmDictRange_Odd; - break; - case 'e': - case 'E': - r = DcmDictRange_Even; - break; - case 'u': - case 'U': - r = DcmDictRange_Unspecified; - break; - default: - DCMDATA_ERROR("DcmDataDictionary: Unknown range restrictor: " << restrictor); - ok = OFFalse; - break; - } - } else if (sscanf(s, "%x-%x", &l, &h) == 2) { - r = DcmDictRange_Even; /* by default */ - } else if (sscanf(s, "%x", &l) == 1) { - h = l; - } else { - ok = OFFalse; - } - return ok; -} - -static OFBool -parseWholeTagField(char* s, DcmTagKey& key, - DcmTagKey& upperKey, - DcmDictRangeRestriction& groupRestriction, - DcmDictRangeRestriction& elementRestriction, - char *&privCreator) -{ - unsigned int gl, gh, el, eh; - groupRestriction = DcmDictRange_Unspecified; - elementRestriction = DcmDictRange_Unspecified; - - stripLeadingWhitespace(s); - stripTrailingWhitespace(s); - - char gs[64]; - char es[64]; - char pc[64]; - size_t slen = strlen(s); - - if (s[0] != '(') return OFFalse; - if (s[slen - 1] != ')') return OFFalse; - if (strchr(s, ',') == NULL) return OFFalse; - - /* separate the group and element parts */ - int i = 1; /* after the '(' */ - int gi = 0; - for (; s[i] != ',' && s[i] != '\0'; i++) - { - gs[gi] = s[i]; - gi++; - } - gs[gi] = '\0'; - - if (s[i] == '\0') return OFFalse; /* element part missing */ - i++; /* after the ',' */ - - stripLeadingWhitespace(s + i); - - int pi = 0; - if (s[i] == '\"') /* private creator */ - { - i++; // skip opening quotation mark - for (; s[i] != '\"' && s[i] != '\0'; i++) pc[pi++] = s[i]; - pc[pi] = '\0'; - if (s[i] == '\0') return OFFalse; /* closing quotation mark missing */ - i++; - stripLeadingWhitespace(s + i); - if (s[i] != ',') return OFFalse; /* element part missing */ - i++; /* after the ',' */ - } - - int ei = 0; - for (; s[i] != ')' && s[i] != '\0'; i++) { - es[ei] = s[i]; - ei++; - } - es[ei] = '\0'; - - /* parse the tag parts into their components */ - stripWhitespace(gs); - if (parseTagPart(gs, gl, gh, groupRestriction) == OFFalse) - return OFFalse; - - stripWhitespace(es); - if (parseTagPart(es, el, eh, elementRestriction) == OFFalse) - return OFFalse; - - if (pi > 0) - { - // copy private creator name - privCreator = new char[strlen(pc) + 1]; // deleted by caller - if (privCreator) strcpy(privCreator,pc); - } - - key.set(OFstatic_cast(unsigned short, gl), OFstatic_cast(unsigned short, el)); - upperKey.set(OFstatic_cast(unsigned short, gh), OFstatic_cast(unsigned short, eh)); - - return OFTrue; -} - -static OFBool -onlyWhitespace(const char* s) -{ - size_t len = strlen(s); - int charsFound = OFFalse; - - for (size_t i = 0; (!charsFound) && (i < len); ++i) { - charsFound = !isspace(OFstatic_cast(unsigned char, s[i])); - } - return (!charsFound)? (OFTrue) : (OFFalse); -} - -static char* -getLine(char* line, int maxLineLen, FILE* f) -{ - char* s; - - s = fgets(line, maxLineLen, f); - - /* strip any trailing white space */ - stripTrailingWhitespace(line); - - return s; -} - -static OFBool -isaCommentLine(const char* s) -{ - OFBool isComment = OFFalse; /* assumption */ - size_t len = strlen(s); - size_t i = 0; - for (i = 0; i < len && isspace(OFstatic_cast(unsigned char, s[i])); ++i) /*loop*/; - isComment = (s[i] == DCM_DICT_COMMENT_CHAR); - return isComment; -} - -OFBool -DcmDataDictionary::reloadDictionaries(OFBool loadBuiltin, OFBool loadExternal) -{ - OFBool result = OFTrue; - clear(); - loadSkeletonDictionary(); - if (loadBuiltin) { - loadBuiltinDictionary(); - dictionaryLoaded = (numberOfEntries() > skeletonCount); - if (!dictionaryLoaded) result = OFFalse; - } - if (loadExternal) { - if (loadExternalDictionaries()) - dictionaryLoaded = OFTrue; - else - result = OFFalse; - } - return result; -} - -OFBool -DcmDataDictionary::loadDictionary(const char* fileName, OFBool errorIfAbsent) -{ - - char lineBuf[DCM_MAXDICTLINESIZE + 1]; - FILE* f = NULL; - int lineNumber = 0; - char* lineFields[DCM_MAXDICTFIELDS + 1]; - int fieldsPresent; - DcmDictEntry* e; - int errorsEncountered = 0; - OFBool errorOnThisLine = OFFalse; - int i; - - DcmTagKey key, upperKey; - DcmDictRangeRestriction groupRestriction = DcmDictRange_Unspecified; - DcmDictRangeRestriction elementRestriction = DcmDictRange_Unspecified; - DcmVR vr; - char* vrName; - char* tagName; - char* privCreator; - int vmMin, vmMax = 1; - const char* standardVersion; - - /* first, check whether 'fileName' really points to a file (and not to a directory or the like) */ - if (!OFStandard::fileExists(fileName) || (f = fopen(fileName, "r")) == NULL) { - if (errorIfAbsent) { - DCMDATA_ERROR("DcmDataDictionary: Cannot open file: " << fileName); - } - return OFFalse; - } - - DCMDATA_DEBUG("DcmDataDictionary: Loading file: " << fileName); - - while (getLine(lineBuf, DCM_MAXDICTLINESIZE, f)) { - lineNumber++; - - if (onlyWhitespace(lineBuf)) { - continue; /* ignore this line */ - } - if (isaCommentLine(lineBuf)) { - continue; /* ignore this line */ - } - - errorOnThisLine = OFFalse; - - /* fields are tab separated */ - fieldsPresent = splitFields(lineBuf, lineFields, - DCM_MAXDICTFIELDS, - DCM_DICT_FIELD_SEPARATOR_CHAR); - - /* initialize dict entry fields */ - vrName = NULL; - tagName = NULL; - privCreator = NULL; - vmMin = vmMax = 1; - standardVersion = "DICOM"; - - switch (fieldsPresent) { - case 0: - case 1: - case 2: - DCMDATA_ERROR("DcmDataDictionary: "<< fileName << ": " - << "too few fields (line " << lineNumber << ")"); - errorOnThisLine = OFTrue; - break; - default: - DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": " - << "too many fields (line " << lineNumber << "): "); - errorOnThisLine = OFTrue; - break; - case 5: - stripWhitespace(lineFields[4]); - standardVersion = lineFields[4]; - /* drop through to next case label */ - case 4: - /* the VM field is present */ - if (!parseVMField(lineFields[3], vmMin, vmMax)) { - DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": " - << "bad VM field (line " << lineNumber << "): " << lineFields[3]); - errorOnThisLine = OFTrue; - } - /* drop through to next case label */ - case 3: - if (!parseWholeTagField(lineFields[0], key, upperKey, - groupRestriction, elementRestriction, privCreator)) - { - DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": " - << "bad Tag field (line " << lineNumber << "): " << lineFields[0]); - errorOnThisLine = OFTrue; - } else { - /* all is OK */ - vrName = lineFields[1]; - stripWhitespace(vrName); - - tagName = lineFields[2]; - stripWhitespace(tagName); - } - } - - if (!errorOnThisLine) { - /* check the VR Field */ - vr.setVR(vrName); - if (vr.getEVR() == EVR_UNKNOWN) { - DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": " - << "bad VR field (line " << lineNumber << "): " << vrName); - errorOnThisLine = OFTrue; - } - } - - if (!errorOnThisLine) { - e = new DcmDictEntry( - key.getGroup(), key.getElement(), - upperKey.getGroup(), upperKey.getElement(), - vr, tagName, vmMin, vmMax, standardVersion, OFTrue, - privCreator); - - e->setGroupRangeRestriction(groupRestriction); - e->setElementRangeRestriction(elementRestriction); - addEntry(e); - } - - for (i = 0; i < fieldsPresent; i++) { - free(lineFields[i]); - lineFields[i] = NULL; - } - - delete[] privCreator; - - if (errorOnThisLine) { - errorsEncountered++; - } - } - - fclose(f); - - /* return OFFalse in case of errors and set internal state accordingly */ - if (errorsEncountered == 0) { - dictionaryLoaded = OFTrue; - return OFTrue; - } - else { - dictionaryLoaded = OFFalse; - return OFFalse; - } -} - -#ifndef HAVE_GETENV - -static -char* getenv() { - return NULL; -} - -#endif /* !HAVE_GETENV */ - - - -OFBool -DcmDataDictionary::loadExternalDictionaries() -{ - const char* env = NULL; - size_t len; - int sepCnt = 0; - OFBool msgIfDictAbsent = OFTrue; - OFBool loadFailed = OFFalse; - - env = getenv(DCM_DICT_ENVIRONMENT_VARIABLE); - if ((env == NULL) || (strlen(env) == 0)) { - env = DCM_DICT_DEFAULT_PATH; - msgIfDictAbsent = OFFalse; - } - - if ((env != NULL) && (strlen(env) != 0)) { - len = strlen(env); - for (size_t i = 0; i < len; ++i) { - if (env[i] == ENVIRONMENT_PATH_SEPARATOR) { - sepCnt++; - } - } - - if (sepCnt == 0) { - if (!loadDictionary(env, msgIfDictAbsent)) { - return OFFalse; - } - } else { - char** dictArray; - - dictArray = OFstatic_cast(char **, malloc((sepCnt + 1) * sizeof(char*))); - - int ndicts = splitFields(env, dictArray, sepCnt + 1, - ENVIRONMENT_PATH_SEPARATOR); - - for (int ii = 0; ii < ndicts; ii++) { - if ((dictArray[ii] != NULL) && (strlen(dictArray[ii]) > 0)) { - if (!loadDictionary(dictArray[ii], msgIfDictAbsent)) { - loadFailed = OFTrue; - } - } - free(dictArray[ii]); - } - free(dictArray); - } - } - - return (loadFailed) ? (OFFalse) : (OFTrue); -} - - -void -DcmDataDictionary::addEntry(DcmDictEntry* e) -{ - if (e->isRepeating()) { - /* - * Find the best position in repeating tag list - * Existing entries are replaced if the ranges and repetition - * constraints are the same. - * If a range represents a subset of an existing range then it - * will be placed before it in the list. This ensures that a - * search will find the subset rather than the superset. - * Otherwise entries are appended to the end of the list. - */ - OFBool inserted = OFFalse; - - DcmDictEntryListIterator iter(repDict.begin()); - DcmDictEntryListIterator last(repDict.end()); - for (; !inserted && iter != last; ++iter) { - if (e->setEQ(**iter)) { - /* replace the old entry with the new */ - DcmDictEntry *old = *iter; - *iter = e; -#ifdef PRINT_REPLACED_DICTIONARY_ENTRIES - DCMDATA_WARN("replacing " << *old); -#endif - delete old; - inserted = OFTrue; - } else if (e->subset(**iter)) { - /* e is a subset of the current list position, insert before */ - repDict.insert(iter, e); - inserted = OFTrue; - } - } - if (!inserted) { - /* insert at end */ - repDict.push_back(e); - inserted = OFTrue; - } - } else { - hashDict.put(e); - } -} - -void -DcmDataDictionary::deleteEntry(const DcmDictEntry& entry) -{ - DcmDictEntry* e = NULL; - e = OFconst_cast(DcmDictEntry *, findEntry(entry)); - if (e != NULL) { - if (e->isRepeating()) { - repDict.remove(e); - delete e; - } else { - hashDict.del(entry.getKey(), entry.getPrivateCreator()); - } - } -} - -const DcmDictEntry* -DcmDataDictionary::findEntry(const DcmDictEntry& entry) const -{ - const DcmDictEntry* e = NULL; - - if (entry.isRepeating()) { - OFBool found = OFFalse; - DcmDictEntryListConstIterator iter(repDict.begin()); - DcmDictEntryListConstIterator last(repDict.end()); - for (; !found && iter != last; ++iter) { - if (entry.setEQ(**iter)) { - found = OFTrue; - e = *iter; - } - } - } else { - e = hashDict.get(entry, entry.getPrivateCreator()); - } - return e; -} - -const DcmDictEntry* -DcmDataDictionary::findEntry(const DcmTagKey& key, const char *privCreator) const -{ - /* search first in the normal tags dictionary and if not found - * then search in the repeating tags list. - */ - const DcmDictEntry* e = NULL; - - e = hashDict.get(key, privCreator); - if (e == NULL) { - /* search in the repeating tags dictionary */ - OFBool found = OFFalse; - DcmDictEntryListConstIterator iter(repDict.begin()); - DcmDictEntryListConstIterator last(repDict.end()); - for (; !found && iter != last; ++iter) { - if ((*iter)->contains(key, privCreator)) { - found = OFTrue; - e = *iter; - } - } - } - return e; -} - -const DcmDictEntry* -DcmDataDictionary::findEntry(const char *name) const -{ - const DcmDictEntry* e = NULL; - const DcmDictEntry* ePrivate = NULL; - - /* search first in the normal tags dictionary and if not found - * then search in the repeating tags list. - */ - DcmHashDictIterator iter; - for (iter = hashDict.begin(); (e == NULL) && (iter != hashDict.end()); ++iter) { - if ((*iter)->contains(name)) { - e = *iter; - if (e->getGroup() % 2) - { - /* tag is a private tag - continue search to be sure to find non-private keys first */ - if (!ePrivate) ePrivate = e; - e = NULL; - } - } - } - - if (e == NULL) { - /* search in the repeating tags dictionary */ - OFBool found = OFFalse; - DcmDictEntryListConstIterator iter2(repDict.begin()); - DcmDictEntryListConstIterator last(repDict.end()); - for (; !found && iter2 != last; ++iter2) { - if ((*iter2)->contains(name)) { - found = OFTrue; - e = *iter2; - } - } - } - - if (e == NULL && ePrivate != NULL) { - /* no standard key found - use the first private key found */ - e = ePrivate; - } - - return e; -} - - -/* ================================================================== */ - - -GlobalDcmDataDictionary::GlobalDcmDataDictionary() - : dataDict(NULL) -#ifdef WITH_THREADS - , dataDictLock() -#endif -{ -} - -GlobalDcmDataDictionary::~GlobalDcmDataDictionary() -{ - /* No threads may be active any more, so no locking needed */ - delete dataDict; -} - -void GlobalDcmDataDictionary::createDataDict() -{ - /* Make sure only one thread tries to initialize the dictionary */ -#ifdef WITH_THREADS - dataDictLock.wrlock(); -#endif -#ifdef DONT_LOAD_EXTERNAL_DICTIONARIES - const OFBool loadExternal = OFFalse; -#else - const OFBool loadExternal = OFTrue; -#endif - /* Make sure no other thread managed to create the dictionary - * before we got our write lock. */ - if (!dataDict) - dataDict = new DcmDataDictionary(OFTrue /*loadBuiltin*/, loadExternal); -#ifdef WITH_THREADS - dataDictLock.unlock(); -#endif -} - -const DcmDataDictionary& GlobalDcmDataDictionary::rdlock() -{ -#ifdef WITH_THREADS - dataDictLock.rdlock(); -#endif - if (!dataDict) - { - /* dataDictLock must not be locked during createDataDict() */ -#ifdef WITH_THREADS - dataDictLock.unlock(); -#endif - createDataDict(); -#ifdef WITH_THREADS - dataDictLock.rdlock(); -#endif - } - return *dataDict; -} - -DcmDataDictionary& GlobalDcmDataDictionary::wrlock() -{ -#ifdef WITH_THREADS - dataDictLock.wrlock(); -#endif - if (!dataDict) - { - /* dataDictLock must not be locked during createDataDict() */ -#ifdef WITH_THREADS - dataDictLock.unlock(); -#endif - createDataDict(); -#ifdef WITH_THREADS - dataDictLock.wrlock(); -#endif - } - return *dataDict; -} - -void GlobalDcmDataDictionary::unlock() -{ -#ifdef WITH_THREADS - dataDictLock.unlock(); -#endif -} - -OFBool GlobalDcmDataDictionary::isDictionaryLoaded() -{ - OFBool result = rdlock().isDictionaryLoaded(); - unlock(); - return result; -} - -void GlobalDcmDataDictionary::clear() -{ - wrlock().clear(); - unlock(); -} - - - - -// Function by the Orthanc project to load a dictionary from a memory -// buffer, which is necessary in sandboxed environments. This is an -// adapted version of DcmDataDictionary::loadDictionary(). - - -#include <boost/noncopyable.hpp> - -struct OrthancLinesIterator; - -// This plain old C class is implemented in "../../Core/Toolbox.h" -OrthancLinesIterator* OrthancLinesIterator_Create(const std::string& content); - -bool OrthancLinesIterator_GetLine(std::string& target, - const OrthancLinesIterator* iterator); - -void OrthancLinesIterator_Next(OrthancLinesIterator* iterator); - -void OrthancLinesIterator_Free(OrthancLinesIterator* iterator); - - -class LinesIterator : public boost::noncopyable -{ -private: - OrthancLinesIterator* iterator_; - -public: - LinesIterator(const std::string& content) : - iterator_(NULL) - { - iterator_ = OrthancLinesIterator_Create(content); - } - - ~LinesIterator() - { - if (iterator_ != NULL) - { - OrthancLinesIterator_Free(iterator_); - iterator_ = NULL; - } - } - - bool GetLine(std::string& target) const - { - if (iterator_ != NULL) - { - return OrthancLinesIterator_GetLine(target, iterator_); - } - else - { - return false; - } - } - - void Next() - { - if (iterator_ != NULL) - { - OrthancLinesIterator_Next(iterator_); - } - } -}; - - - -OFBool -DcmDataDictionary::loadFromMemory(const std::string& content, OFBool errorIfAbsent) -{ - int lineNumber = 0; - char* lineFields[DCM_MAXDICTFIELDS + 1]; - int fieldsPresent; - DcmDictEntry* e; - int errorsEncountered = 0; - OFBool errorOnThisLine = OFFalse; - int i; - - DcmTagKey key, upperKey; - DcmDictRangeRestriction groupRestriction = DcmDictRange_Unspecified; - DcmDictRangeRestriction elementRestriction = DcmDictRange_Unspecified; - DcmVR vr; - char* vrName; - char* tagName; - char* privCreator; - int vmMin, vmMax = 1; - const char* standardVersion; - - LinesIterator iterator(content); - - std::string line; - while (iterator.GetLine(line)) { - iterator.Next(); - - if (line.size() >= DCM_MAXDICTLINESIZE) { - DCMDATA_ERROR("DcmDataDictionary: Too long line: " << line); - continue; - } - - lineNumber++; - - if (onlyWhitespace(line.c_str())) { - continue; /* ignore this line */ - } - if (isaCommentLine(line.c_str())) { - continue; /* ignore this line */ - } - - errorOnThisLine = OFFalse; - - /* fields are tab separated */ - fieldsPresent = splitFields(line.c_str(), lineFields, - DCM_MAXDICTFIELDS, - DCM_DICT_FIELD_SEPARATOR_CHAR); - - /* initialize dict entry fields */ - vrName = NULL; - tagName = NULL; - privCreator = NULL; - vmMin = vmMax = 1; - standardVersion = "DICOM"; - - switch (fieldsPresent) { - case 0: - case 1: - case 2: - DCMDATA_ERROR("DcmDataDictionary: " - << "too few fields (line " << lineNumber << ")"); - errorOnThisLine = OFTrue; - break; - default: - DCMDATA_ERROR("DcmDataDictionary: " - << "too many fields (line " << lineNumber << "): "); - errorOnThisLine = OFTrue; - break; - case 5: - stripWhitespace(lineFields[4]); - standardVersion = lineFields[4]; - /* drop through to next case label */ - case 4: - /* the VM field is present */ - if (!parseVMField(lineFields[3], vmMin, vmMax)) { - DCMDATA_ERROR("DcmDataDictionary: " - << "bad VM field (line " << lineNumber << "): " << lineFields[3]); - errorOnThisLine = OFTrue; - } - /* drop through to next case label */ - case 3: - if (!parseWholeTagField(lineFields[0], key, upperKey, - groupRestriction, elementRestriction, privCreator)) - { - DCMDATA_ERROR("DcmDataDictionary: " - << "bad Tag field (line " << lineNumber << "): " << lineFields[0]); - errorOnThisLine = OFTrue; - } else { - /* all is OK */ - vrName = lineFields[1]; - stripWhitespace(vrName); - - tagName = lineFields[2]; - stripWhitespace(tagName); - } - } - - if (!errorOnThisLine) { - /* check the VR Field */ - vr.setVR(vrName); - if (vr.getEVR() == EVR_UNKNOWN) { - DCMDATA_ERROR("DcmDataDictionary: " - << "bad VR field (line " << lineNumber << "): " << vrName); - errorOnThisLine = OFTrue; - } - } - - if (!errorOnThisLine) { - e = new DcmDictEntry( - key.getGroup(), key.getElement(), - upperKey.getGroup(), upperKey.getElement(), - vr, tagName, vmMin, vmMax, standardVersion, OFTrue, - privCreator); - - e->setGroupRangeRestriction(groupRestriction); - e->setElementRangeRestriction(elementRestriction); - addEntry(e); - } - - for (i = 0; i < fieldsPresent; i++) { - free(lineFields[i]); - lineFields[i] = NULL; - } - - delete[] privCreator; - - if (errorOnThisLine) { - errorsEncountered++; - } - } - - /* return OFFalse in case of errors and set internal state accordingly */ - if (errorsEncountered == 0) { - dictionaryLoaded = OFTrue; - return OFTrue; - } - else { - dictionaryLoaded = OFFalse; - return OFFalse; - } -}
--- a/Resources/WebAssembly/dcdict.h Mon Dec 23 15:46:50 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,302 +0,0 @@ -/* - * - * Copyright (C) 1994-2015, OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were developed by - * - * OFFIS e.V. - * R&D Division Health - * Escherweg 2 - * D-26121 Oldenburg, Germany - * - * - * Module: dcmdata - * - * Author: Andrew Hewett - * - * Purpose: Interface for loadable DICOM data dictionary - * - */ - - -#ifndef DCMDICT_H -#define DCMDICT_H - -#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ - -#include "dcmtk/ofstd/ofthread.h" -#include "dcmtk/dcmdata/dchashdi.h" - -/// maximum length of a line in the loadable DICOM dictionary -#define DCM_MAXDICTLINESIZE 2048 - -/// maximum number of fields per entry in the loadable DICOM dictionary -#define DCM_MAXDICTFIELDS 6 - -/// environment variable pointing to the data dictionary file -#define DCM_DICT_ENVIRONMENT_VARIABLE "DCMDICTPATH" - -#ifndef DCM_DICT_DEFAULT_PATH -/* -** The default dictionary path is system dependent. It should -** be defined in a configuration file included from "osconfig.h" -*/ -#error "DCM_DICT_DEFAULT_PATH is not defined via osconfig.h" -#endif /* !DCM_DICT_DEFAULT_PATH */ - -#ifndef ENVIRONMENT_PATH_SEPARATOR -#define ENVIRONMENT_PATH_SEPARATOR '\n' /* at least define something unlikely */ -#endif - - -/** this class implements a loadable DICOM Data Dictionary - */ -class DCMTK_DCMDATA_EXPORT DcmDataDictionary -{ -public: - - /** constructor - * @param loadBuiltin flag indicating if a built-in data dictionary - * (if any) should be loaded. - * @param loadExternal flag indicating if an external data dictionary - * should be read from file. - */ - DcmDataDictionary(OFBool loadBuiltin, OFBool loadExternal); - - /// destructor - ~DcmDataDictionary(); - - /** checks if a data dictionary is loaded (excluding the skeleton dictionary) - * @return true if loaded, false if no dictionary is present - */ - OFBool isDictionaryLoaded() const { return dictionaryLoaded; } - - /// returns the number of normal (non-repeating) tag entries - int numberOfNormalTagEntries() const { return hashDict.size(); } - - /// returns the number of repeating tag entries - int numberOfRepeatingTagEntries() const { return OFstatic_cast(int, repDict.size()); } - - /** returns the number of dictionary entries that were loaded - * either from file or from a built-in dictionary or both. - */ - int numberOfEntries() const - { return numberOfNormalTagEntries() - + numberOfRepeatingTagEntries() - skeletonCount; } - - /** returns the number of skeleton entries. The skeleton is a collection - * of dictionary entries which are always present, even if neither internal - * nor external dictionary have been loaded. It contains very basic - * things like item delimitation and sequence delimitation. - */ - int numberOfSkeletonEntries() const { return skeletonCount; } - - /** reload data dictionaries. First, all dictionary entries are deleted. - * @param loadBuiltin flag indicating if a built-in data dictionary - * (if any) should be loaded. - * @param loadExternal flag indicating if an external data dictionary - * should be read from file. - * @return true if reload was successful, false if an error occurred - */ - OFBool reloadDictionaries(OFBool loadBuiltin, OFBool loadExternal); - - /** load a particular dictionary from file. - * @param fileName filename - * @param errorIfAbsent causes the method to return false - * if the file cannot be opened - * @return false if the file contains a parse error or if the file could - * not be opened and errorIfAbsent was set, true otherwise. - */ - OFBool loadDictionary(const char* fileName, OFBool errorIfAbsent = OFTrue); - - /** dictionary lookup for the given tag key and private creator name. - * First the normal tag dictionary is searched. If not found - * then the repeating tag dictionary is searched. - * @param key tag key - * @param privCreator private creator name, may be NULL - */ - const DcmDictEntry* findEntry(const DcmTagKey& key, const char *privCreator) const; - - /** dictionary lookup for the given attribute name. - * First the normal tag dictionary is searched. If not found - * then the repeating tag dictionary is searched. - * Only considers standard attributes (i. e. without private creator) - * @param name attribute name - */ - const DcmDictEntry* findEntry(const char *name) const; - - /// deletes all dictionary entries - void clear(); - - /** adds an entry to the dictionary. Must be allocated via new. - * The entry becomes the property of the dictionary and will be - * deallocated (via delete) upon clear() or dictionary destruction. - * If an equivalent entry already exists it will be replaced by - * the new entry and the old entry deallocated (via delete). - * @param entry pointer to new entry - */ - void addEntry(DcmDictEntry* entry); - - /* Iterators to access the normal and the repeating entries */ - - /// returns an iterator to the start of the normal (non-repeating) dictionary - DcmHashDictIterator normalBegin() { return hashDict.begin(); } - - /// returns an iterator to the end of the normal (non-repeating) dictionary - DcmHashDictIterator normalEnd() { return hashDict.end(); } - - /// returns an iterator to the start of the repeating tag dictionary - DcmDictEntryListIterator repeatingBegin() { return repDict.begin(); } - - /// returns an iterator to the end of the repeating tag dictionary - DcmDictEntryListIterator repeatingEnd() { return repDict.end(); } - - // Function by the Orthanc project to load a dictionary from a - // memory buffer, which is necessary in sandboxed - // environments. This is an adapted version of - // DcmDataDictionary::loadDictionary(). - OFBool loadFromMemory(const std::string& content, OFBool errorIfAbsent = OFTrue); - -private: - - /** private undefined assignment operator - */ - DcmDataDictionary &operator=(const DcmDataDictionary &); - - /** private undefined copy constructor - */ - DcmDataDictionary(const DcmDataDictionary &); - - /** loads external dictionaries defined via environment variables - * @return true if successful - */ - OFBool loadExternalDictionaries(); - - /** loads a builtin (compiled) data dictionary. - * Depending on which code is in use, this function may not - * do anything. - */ - void loadBuiltinDictionary(); - - /** loads the skeleton dictionary (the bare minimum needed to run) - * @return true if successful - */ - OFBool loadSkeletonDictionary(); - - /** looks up the given directory entry in the two dictionaries. - * @return pointer to entry if found, NULL otherwise - */ - const DcmDictEntry* findEntry(const DcmDictEntry& entry) const; - - /** deletes the given entry from either dictionary - */ - void deleteEntry(const DcmDictEntry& entry); - - - /** dictionary of normal tags - */ - DcmHashDict hashDict; - - /** dictionary of repeating tags - */ - DcmDictEntryList repDict; - - /** the number of skeleton entries - */ - int skeletonCount; - - /** is a dictionary loaded (more than skeleton) - */ - OFBool dictionaryLoaded; - -}; - - -/** global singleton dicom dictionary that is used by DCMTK in order to lookup - * attribute VR, tag names and so on. The dictionary is internally populated - * on first use, if the user accesses it via rdlock() or wrlock(). The - * dictionary allows safe read (shared) and write (exclusive) access from - * multiple threads in parallel. - */ -class DCMTK_DCMDATA_EXPORT GlobalDcmDataDictionary -{ -public: - /** constructor. - */ - GlobalDcmDataDictionary(); - - /** destructor - */ - ~GlobalDcmDataDictionary(); - - /** acquires a read lock and returns a const reference to - * the dictionary. - * @return const reference to dictionary - */ - const DcmDataDictionary& rdlock(); - - /** acquires a write lock and returns a non-const reference - * to the dictionary. - * @return non-const reference to dictionary. - */ - DcmDataDictionary& wrlock(); - - /** unlocks the read or write lock which must have been acquired previously. - */ - void unlock(); - - /** checks if a data dictionary has been loaded. This method acquires and - * releases a read lock. It must not be called with another lock on the - * dictionary being held by the calling thread. - * @return OFTrue if dictionary has been loaded, OFFalse otherwise. - */ - OFBool isDictionaryLoaded(); - - /** erases the contents of the dictionary. This method acquires and - * releases a write lock. It must not be called with another lock on the - * dictionary being held by the calling thread. This method is intended - * as a help for debugging memory leaks. - */ - void clear(); - -private: - /** private undefined assignment operator - */ - GlobalDcmDataDictionary &operator=(const GlobalDcmDataDictionary &); - - /** private undefined copy constructor - */ - GlobalDcmDataDictionary(const GlobalDcmDataDictionary &); - - /** create the data dictionary instance for this class. Used for first - * intialization. The caller must not have dataDictLock locked. - */ - void createDataDict(); - - /** the data dictionary managed by this class - */ - DcmDataDictionary *dataDict; - -#ifdef WITH_THREADS - /** the read/write lock used to protect access from multiple threads - */ - OFReadWriteLock dataDictLock; -#endif -}; - - -/** The Global DICOM Data Dictionary. - * Will be created before main() starts and gets populated on its first use. - * Tries to load a builtin data dictionary (if compiled in). - * Tries to load data dictionaries from files specified by - * the DCMDICTPATH environment variable. If this environment - * variable does not exist then a default file is loaded (if - * it exists). - * It is possible that no data dictionary gets loaded. This - * is likely to cause unexpected behaviour in the dcmdata - * toolkit classes. - */ -extern DCMTK_DCMDATA_EXPORT GlobalDcmDataDictionary dcmDataDict; - -#endif