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