dcmpschk.cxx
Upload User: jinxing
Upload Date: 2016-11-26
Package Size: 5101k
Code Size: 37k
Category:

Picture Viewer

Development Platform:

Visual C++

  1. /*
  2.  *
  3.  *  Copyright (C) 2000-2004, OFFIS
  4.  *
  5.  *  This software and supporting documentation were developed by
  6.  *
  7.  *    Kuratorium OFFIS e.V.
  8.  *    Healthcare Information and Communication Systems
  9.  *    Escherweg 2
  10.  *    D-26121 Oldenburg, Germany
  11.  *
  12.  *  THIS SOFTWARE IS MADE AVAILABLE,  AS IS,  AND OFFIS MAKES NO  WARRANTY
  13.  *  REGARDING  THE  SOFTWARE,  ITS  PERFORMANCE,  ITS  MERCHANTABILITY  OR
  14.  *  FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES  OR
  15.  *  ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND
  16.  *  PERFORMANCE OF THE SOFTWARE IS WITH THE USER.
  17.  *
  18.  *  Module: dcmpstat
  19.  *
  20.  *  Author: Andrew Hewett/Marco Eichelberg
  21.  *
  22.  *  Purpose:
  23.  *    VR and IOD checker for Presentation States
  24.  *
  25.  *
  26.  *  Last Update:      $Author: joergr $
  27.  *  Update Date:      $Date: 2004/02/04 15:44:38 $
  28.  *  CVS/RCS Revision: $Revision: 1.16 $
  29.  *  Status:           $State: Exp $
  30.  *
  31.  *  CVS/RCS Log at end of file
  32.  *
  33.  */
  34. #include "osconfig.h"    /* make sure OS specific configuration is included first */
  35. #define INCLUDE_CSTDLIB
  36. #include "ofstdinc.h"
  37. #ifdef HAVE_WINDOWS_H
  38. #include <windows.h>     /* this includes either winsock.h or winsock2.h */
  39. #endif
  40. #ifdef HAVE_GUSI_H
  41.     /* needed for Macintosh */
  42. #include <GUSI.h>
  43. #include <SIOUX.h>
  44. #endif
  45. #include "ofstream.h"
  46. #include "dctk.h"        /* for class DcmDataset */
  47. #include "ofstring.h"    /* for class OFString */
  48. #include "vrscan.h"
  49. #include "ofconapp.h"    /* for OFConsoleApplication */
  50. #include "dcmpstat.h"    /* for DcmPresentationState */
  51. #ifdef WITH_ZLIB
  52. #include <zlib.h>        /* for zlibVersion() */
  53. #endif
  54. #define OFFIS_CONSOLE_APPLICATION "dcmpschk"
  55. static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v"
  56.   OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $";
  57. /* command line options */
  58. static OFBool           opt_verbose         = OFFalse;             /* default: not verbose */
  59. static const char *     opt_logfilename     = NULL;
  60. static const char *     opt_filename        = NULL;
  61. static int              opt_debugMode       = 0;
  62. static ostream *        logstream           = &COUT;
  63. // ********************************************
  64. enum ErrorMode
  65. {
  66.   EM_ok,
  67.   EM_informational,
  68.   EM_warning,
  69.   EM_error
  70. };
  71. #define MSG_invalidVR      "Error: Unsupported Value Representation."
  72. #define MSG_lengthtoolarge "Error: Value Length too large."
  73. #define MSG_lengthtoosmall "Error: Value Length too small."
  74. #define MSG_unexpectedVR   "Error: Unexpected Value Representation."
  75. #define MSG_vmtoolarge     "Error: Value Multiplicity too large."
  76. #define MSG_vmtoosmall     "Error: Value Multiplicity too small."
  77. #define MSGe_mhxferError   "Error: Meta-header encoded using invalid transfer syntax."
  78. #define MSGe_missingAtt    "Error: Attribute is missing."
  79. #define MSGe_wrongAtt      "Error: Attribute is incorrect."
  80. #define MSGe_wrongDType    "Error: Attribute value does not conform to data type definition."
  81. #define MSGi_wrongDType    "Informational: Attribute value does not conform to data type definition."
  82. #define MSGw_wrongDType    "Warning: Attribute value uses retired form."
  83. void printVRError(
  84.   ostream& out,
  85.   ErrorMode mode,
  86.   const char *elementDescription,
  87.   const DcmDictEntry* dictRef,
  88.   const char *format)
  89. {
  90.   if (mode == EM_error)              out << MSGe_wrongDType << endl;
  91.   else if (mode == EM_warning)       out << MSGw_wrongDType << endl;
  92.   else if (mode == EM_informational) out << MSGi_wrongDType << endl;
  93.   out << "   Affected attribute: ";
  94.   if (dictRef)
  95.   {
  96.      out << dictRef->getTagName();
  97.      out << " " << dictRef->getKey();
  98.      out << ", Type " << dictRef->getVR().getVRName() << endl;
  99.   } else out << "(unnamed)" << endl;
  100.   out << "   Attribute value   : ";
  101.   if (elementDescription) out << "[" << elementDescription << "]" << endl;
  102.   else out << "(empty)" << endl;
  103.   if (mode == EM_error)
  104.   {
  105.     out << "   Expected format for each value: ";
  106.     if (format)
  107.     {
  108.       out << format << endl;
  109.     } else out << "(undefined)" << endl;
  110.   }
  111.   out << endl;
  112. }
  113. void printResult(ostream& out, DcmStack& stack, OFBool showFullData)
  114. {
  115.     unsigned long n = stack.card();
  116.     if (n == 0) {
  117.         return;
  118.     }
  119.     /* print the path leading up to the top stack elem */
  120.     for (unsigned long i=n-1; i>=1; i--)
  121.     {
  122.         DcmObject *dobj = stack.elem(i);
  123.         /* do not print if a DCM_Item as this is not
  124.          * very helpful to distinguish instances.
  125.          */
  126.         if (dobj != NULL && dobj->getTag().getXTag() != DCM_Item)
  127.         {
  128.             char buf[128];
  129.             sprintf(buf, "(%04x,%04x).",
  130.                     (unsigned)dobj->getGTag(),
  131.                     (unsigned)dobj->getETag());
  132.             out << buf;
  133.         }
  134.     }
  135.     /* print the tag and its value */
  136.     DcmObject *dobj = stack.top();
  137.     dobj->print(out, (showFullData ? 0 : DCMTypes::PF_shortenLongTagValues));
  138. }
  139. OFBool isaStringVR(DcmVR& vr)
  140. {
  141.     OFBool isaString = OFFalse;
  142.     switch(vr.getEVR())
  143.     {
  144.     case EVR_AE:
  145.     case EVR_AS:
  146.     case EVR_CS:
  147.     case EVR_DA:
  148.     case EVR_DS:
  149.     case EVR_DT:
  150.     case EVR_IS:
  151.     case EVR_LO:
  152.     case EVR_LT:
  153.     case EVR_PN:
  154.     case EVR_SH:
  155.     case EVR_ST:
  156.     case EVR_TM:
  157.     case EVR_UI:
  158.         isaString = OFTrue;
  159.         break;
  160.     default:
  161.         isaString = OFFalse;
  162.         break;
  163.     }
  164.     return isaString;
  165. }
  166. const char* streamvm(const DcmDictEntry *e)
  167. {
  168.     static char buf[256];
  169.     if (e->isFixedSingleVM()) {
  170.         sprintf(buf, "%d", e->getVMMax());
  171.     } else if (e->isVariableRangeVM()) {
  172.         sprintf(buf, "%d-n", e->getVMMin());
  173.     } else if (e->isFixedRangeVM()){
  174.         sprintf(buf, "%d-%d", e->getVMMin(), e->getVMMax());
  175.     } else {
  176.         sprintf(buf, "?(%d-%d)?", e->getVMMin(), e->getVMMax());
  177.     }
  178.     return buf;
  179. }
  180. const char* streamLengthOfValue(DcmVR& vr)
  181. {
  182.     static char buf[256];
  183.     Uint32 min = vr.getMinValueLength();
  184.     Uint32 max = vr.getMaxValueLength();
  185.     Uint32 undefLen = DCM_UndefinedLength;
  186.     if (min==max) {
  187.         sprintf(buf, "%d bytes fixed length", (int)min);
  188.     } else if (min==0) {
  189.         if (max==undefLen) {
  190.             sprintf(buf, "unrestricted length");
  191.         } else {
  192.             sprintf(buf, "%d bytes maximum", (int)max);
  193.         }
  194.     } else {
  195.         sprintf(buf, "range %d-%d bytes length", (int)min, (int)max);
  196.     }
  197.     return buf;
  198. }
  199. int
  200. splitFields(char* line, char* fields[], Uint32 maxFields, char splitChar)
  201. {
  202.     char* p;
  203.     Uint32 foundFields = 0;
  204.     int len;
  205.     do {
  206.         p = strchr(line, splitChar);
  207.         if (p == NULL) {
  208.             len = strlen(line);
  209.         } else {
  210.             len = p - line;
  211.         }
  212.         fields[foundFields] = new char[len+1];
  213.         strncpy(fields[foundFields], line, len);
  214.         fields[foundFields][len] = '';
  215.         foundFields++;
  216.         line = p + 1;
  217.     } while ((foundFields < maxFields) && (p != NULL));
  218.     return foundFields;
  219. }
  220. OFBool isaKnownPointer(DcmTag& t)
  221. {
  222.     /*
  223.     ** The DICOMDIR code automatically converts any pointers
  224.     ** to have VR=up even if when the data has an explicit VR encoding.
  225.     ** If this attribute is a known pointer then it is ok for it to
  226.     ** have the internal VR=up.
  227.     */
  228.     OFBool result = OFFalse;
  229.     const DcmDataDictionary& globalDataDict = dcmDataDict.rdlock();
  230.     const DcmDictEntry *dictRef = globalDataDict.findEntry(t, NULL);
  231.     if (dictRef && (t.getEVR() == EVR_up) && (t.getEVR() == dictRef->getEVR())) result = OFTrue;
  232.     dcmDataDict.unlock();
  233.     return result;
  234. }
  235. int scanValue(istream& scannerInput)
  236. {
  237.    vrscan valueScanner;
  238.    valueScanner.yyin = &scannerInput;
  239.    int firstResult = valueScanner.yylex();
  240.    if (valueScanner.yylex())
  241.       return 16;
  242.    return firstResult;
  243. }
  244. int checkelem(ostream & out, DcmElement *elem,  DcmXfer& oxfer,
  245.         DcmStack& stack, OFBool showFullData,
  246.         int& dderrors, OFBool /* verbose */)
  247. {
  248.     DcmVR vr(elem->getVR());
  249.     Uint32 len = elem->getLength();
  250.     DcmTag tag(elem->getTag());
  251.     Uint32 vm = elem->getVM();
  252.     const DcmDataDictionary& globalDataDict = dcmDataDict.rdlock();
  253.     const DcmDictEntry *dictRef = globalDataDict.findEntry(tag, NULL);
  254.     int i = 0;
  255.     /*
  256.     ** if the data was encoded in explicit VR then check that the given VR matches
  257.     ** the value in the data dictionary.
  258.     */
  259.     if (oxfer.isExplicitVR()) {
  260.         if (!vr.isStandard() && !isaKnownPointer(tag)) {
  261.             out << MSG_invalidVR << endl
  262.                 << "   Affected VR       : [" << vr.getVRName() << "]";
  263.             if (dictRef) out << ", should be ["
  264.                 << dictRef->getVR().getVRName() << "] according to data dictionary.";
  265.             out << endl << "   Affected attribute: ";
  266.             printResult(out, stack, showFullData);
  267.             out << endl;
  268.             dderrors++;
  269.         } else if (dictRef && !vr.isEquivalent(dictRef->getVR())) {
  270.             out << MSG_unexpectedVR << endl
  271.                 << "   Affected VR       : [" << vr.getVRName() << "], should be ["
  272.                 << dictRef->getVR().getVRName() << "] according to data dictionary." << endl
  273.                 << "   Affected attribute: ";
  274.             printResult(out, stack, showFullData);
  275.             out << endl;
  276.             dderrors++;
  277.         }
  278.     }
  279.     if (len) /* type 2 attributes can be empty. */
  280.     {
  281.        /*
  282.        ** Check value multiplicity
  283.        */
  284.        if ((dictRef)&&(vm < (Uint32)dictRef->getVMMin()))
  285.        {
  286.            out << MSG_vmtoosmall << endl
  287.                << "   Affected VM       : " << vm << ", should be "
  288.                << streamvm(dictRef) << " according to data dictionary."
  289.                << endl << "   Affected attribute: ";
  290.            printResult(out, stack, showFullData);
  291.            out << endl;
  292.            dderrors++;
  293.        }
  294.        if ((dictRef)&&(vm > (Uint32)dictRef->getVMMax()))
  295.        {
  296.            out << MSG_vmtoolarge << endl
  297.                << "   Affected VM       : " << vm << ", should be "
  298.                << streamvm(dictRef) << " according to data dictionary."
  299.                << endl << "   Affected attribute: ";
  300.            printResult(out, stack, showFullData);
  301.            out << endl;
  302.            dderrors++;
  303.        }
  304.        /*
  305.        ** Check length of attribute
  306.        */
  307.        /* Need to split the value into its components if VM>1 */
  308.        if (isaStringVR(vr)) {
  309.            /* only strings have variable length components */
  310.            char* value = NULL;
  311.         ((DcmByteString*)elem)->getString(value);
  312.            char **fields = new char*[vm+1];
  313.            if (fields == NULL) {
  314.                printResult(out, stack, showFullData);
  315.                out << "Internal error: out of memory (value multiplicity too large)" << endl;
  316.            } else {
  317.                int nfields = splitFields(value, fields, vm, '\');
  318.                if ((Uint32)nfields != vm) {
  319.                    printResult(out, stack, showFullData);
  320.                    out << "Internal error: splitFields inconsistency ("
  321.                        << nfields << "!=" << vm << ")" << endl;
  322.                    exit(1);
  323.                }
  324.                for (i=0; (Uint32)i<vm; i++) {
  325.                    char* s = fields[i];
  326.                    int slen = strlen(s);
  327.                    if ((Uint32)slen > vr.getMaxValueLength()) {
  328.                        out << MSG_lengthtoolarge << endl
  329.                            << "   Affected length   : " << slen << " bytes, should be "
  330.                            << streamLengthOfValue(vr) << " for " << vr.getVRName() << "." << endl;
  331.                     if (vm > 1) out << "   Affected value [" << i << "]: "" << s << """ << endl;
  332.                        out << "   Affected attribute: ";
  333.                        printResult(out, stack, showFullData);
  334.                     dderrors++;
  335.                     out << endl;
  336.                    }
  337.                    if ((Uint32)slen < vr.getMinValueLength()) {
  338.                        out << MSG_lengthtoosmall << endl
  339.                            << "   Affected length   : " << slen << " bytes, should be "
  340.                            << streamLengthOfValue(vr) << " for " << vr.getVRName() << "." << endl;
  341.                     if (vm > 1) out << "   Affected value [" << i << "]: "" << s << """ << endl;
  342.                        out << "   Affected attribute: ";
  343.                        printResult(out, stack, showFullData);
  344.                     dderrors++;
  345.                     out << endl;
  346.                    }
  347.                    delete[] fields[i];
  348.                    fields[i] = NULL;
  349.                }
  350.                delete[] fields;
  351.            }
  352.        } else {
  353.            Uint32 componentSize = len; /* vm is 0 if value field is too short, e.g. < 8 bytes for FD */
  354.            if (vm>0) componentSize = len/vm;
  355.            if (componentSize > vr.getMaxValueLength()) {
  356.                out << MSG_lengthtoolarge << endl
  357.                    << "   Affected length   : " << componentSize << " bytes, should be "
  358.                    << streamLengthOfValue(vr) << " for " << vr.getVRName() << "." << endl
  359.                    << "   Affected attribute: ";
  360.                printResult(out, stack, showFullData);
  361.             dderrors++;
  362.             out << endl;
  363.         }
  364.            if (componentSize < vr.getMinValueLength()) {
  365.                out << MSG_lengthtoosmall << endl
  366.                    << "   Affected length   : " << componentSize << " bytes, should be "
  367.                    << streamLengthOfValue(vr) << " for " << vr.getVRName() << "." << endl
  368.                    << "   Affected attribute: ";
  369.                printResult(out, stack, showFullData);
  370.             dderrors++;
  371.             out << endl;
  372.            }
  373.        }
  374.       /*
  375.       **  check, wether the value of the element is suitable to the data type.
  376.       */
  377.       /*** differenciate all value representations */
  378.        if (isaStringVR(vr))
  379.        {
  380.          char* value = NULL;
  381.          if (EC_Normal == ((DcmByteString*)elem)->getString(value) && value) switch (vr.getEVR())
  382.          {
  383.            case EVR_AE:
  384.               {
  385.                 OFString vrAndValue("ae");
  386.                 vrAndValue += value;
  387.                 OFIStringStream input((char*)(vrAndValue.c_str()));
  388.                 int realVR = scanValue(input);
  389.                 if (realVR != 13)
  390.                 {
  391.                    printVRError(out, EM_error, value, dictRef, "all but control characters");
  392.                    dderrors++;
  393.                 }
  394.               }
  395.              break;
  396.            case EVR_AS:
  397.               {
  398.                 OFString vrAndValue("as");
  399.                 vrAndValue += value;
  400.                 OFIStringStream input((char*)(vrAndValue.c_str()));
  401.                 int realVR = scanValue(input);
  402.                 if (realVR != 1)
  403.                 {
  404.                    printVRError(out, EM_error, value, dictRef, "[0-9]{3}[DWMY]");
  405.                    dderrors++;
  406.                 }
  407.               }
  408.               break;
  409.            case EVR_CS:
  410.               {
  411.                 OFString vrAndValue("cs");
  412.                 vrAndValue += value;
  413.                 OFIStringStream input((char*)(vrAndValue.c_str()));
  414.                 int realVR = scanValue(input);
  415.                 if (realVR != 10)
  416.                 {
  417.                    printVRError(out, EM_error, value, dictRef, "[0-9A-Z _]+");
  418.                    dderrors++;
  419.                 }
  420.              }
  421.              break;
  422.            case EVR_DA:
  423.               {
  424.                 OFString vrAndValue("da");
  425.                 vrAndValue += value;
  426.                 OFIStringStream input((char*)(vrAndValue.c_str()));
  427.                 int realVR = scanValue(input);
  428.                 if (realVR != 2 )
  429.                 {
  430.                    if (realVR == 3)
  431.                    {
  432.                       printVRError(out, EM_warning, value, dictRef, NULL);
  433.                       dderrors++;
  434.                    } else {
  435.                      printVRError(out, EM_error, value, dictRef, "[0-9]{8} with valid values for year, month and day");
  436.                      dderrors++;
  437.                    }
  438.                 }
  439.               }
  440.               break;
  441.            case EVR_DS:
  442.               {
  443.                 OFString vrAndValue("ds");
  444.                 vrAndValue += value;
  445.                 OFIStringStream input((char*)(vrAndValue.c_str()));
  446.                 int realVR = scanValue(input);
  447.                 if (realVR != 6)
  448.                 {
  449.                    printVRError(out, EM_error, value, dictRef, "([\-\+]?[0-9]*[\.]?[0-9]+)|([\-\+]?[0-9][\.]?[0-9]+[Ee][\+\-][0-9]+)");
  450.                    dderrors++;
  451.                 }
  452.            }
  453.            break;
  454.         case EVR_DT:
  455.            {
  456.              OFString vrAndValue("dt");
  457.              vrAndValue += value;
  458.              OFIStringStream input((char*)(vrAndValue.c_str()));
  459.              int realVR = scanValue(input);
  460.              if (realVR != 7)
  461.              {
  462.                 printVRError(out, EM_error, value, dictRef, "[0-9]{8}[0-9]{2}([0-9]{2}([0-9]{2}(\.[0-9]{1,6})?)?)?([\+\-][0-9]{4})?");
  463.                 dderrors++;
  464.              }
  465.            }
  466.            break;
  467.         case EVR_IS:
  468.            {
  469.              OFString vrAndValue("is");
  470.              vrAndValue += value;
  471.              OFIStringStream input((char*)(vrAndValue.c_str()));
  472.              int realVR = scanValue(input);
  473.              if (realVR != 8)
  474.              {
  475.                 printVRError(out, EM_error, value, dictRef, "[\+\-]?[0-9]+ in the range -2^31 .. 2^31-1");
  476.                 dderrors++;
  477.              }
  478.            }
  479.            break;
  480.         case EVR_SH:
  481.         case EVR_LO:
  482.            {
  483.              OFString vrAndValue("lo");
  484.              vrAndValue += value;
  485.              OFIStringStream input((char*)(vrAndValue.c_str()));
  486.              int realVR = scanValue(input);
  487.              if (realVR != 12)
  488.              {
  489.                 printVRError(out, EM_error, value, dictRef, "all but '\' and control characters");
  490.                 dderrors++;
  491.              }
  492.            }
  493.            break;
  494.         case EVR_ST:
  495.         case EVR_LT:
  496.            {
  497.              OFString vrAndValue("lt");
  498.              vrAndValue += value;
  499.              OFIStringStream input((char*)(vrAndValue.c_str()));
  500.              int realVR = scanValue(input);
  501.              if (realVR != 14)
  502.              {
  503.                 printVRError(out, EM_error, value, dictRef, "all");
  504.                 dderrors++;
  505.              }
  506.            }
  507.            break;
  508.         case EVR_PN:
  509.            {
  510.              OFString vrAndValue("pn");
  511.              vrAndValue += value;
  512.              OFIStringStream input((char*)(vrAndValue.c_str()));
  513.              int realVR = scanValue(input);
  514.              if (realVR != 11)
  515.              {
  516.                 if (realVR == 15) /* OLD_PN */
  517.                 {
  518.                   printVRError(out, EM_warning, value, dictRef, NULL);
  519.                   dderrors++;
  520.                 } else {
  521.                   printVRError(out, EM_error, value, dictRef, "{all}*([\^]{all}*([\^]{all}*([\^]{all}*(\^{all}*)?)?)?)?");
  522.                   dderrors++;
  523.                 }
  524.              }
  525.            }
  526.            break;
  527.         case EVR_TM:
  528.            {
  529.              OFString vrAndValue("tm");
  530.              vrAndValue += value;
  531.              OFIStringStream input((char*)(vrAndValue.c_str()));
  532.              int realVR = scanValue(input);
  533.              if (realVR != 4)
  534.              {
  535.                 if (realVR == 5)
  536.                 {
  537.                   printVRError(out, EM_warning, value, dictRef, NULL);
  538.                   dderrors++;
  539.                 } else {
  540.                   printVRError(out, EM_error, value, dictRef, "[0-9]{2}([0-9]{2}([0-9]{2}(\.[0-9]{1,6})?)?)? with valid values for hour, minute and second");
  541.                   dderrors++;
  542.                 }
  543.              }
  544.            }
  545.            break;
  546.         case EVR_UI:
  547.            {
  548.              OFString vrAndValue("ui");
  549.              vrAndValue += value;
  550.              OFIStringStream input((char*)(vrAndValue.c_str()));
  551.              int realVR = scanValue(input);
  552.              if (realVR != 9)
  553.              {
  554.                 printVRError(out, EM_error, value, dictRef, "([0-9]+\.)*[0-9]+ without any leading zeroes");
  555.                 dderrors++;
  556.              }
  557.            }
  558.            break;
  559.         default:
  560.            break;
  561.         }
  562.       } //end of if (isaStringVR(vr))
  563.     }
  564.     dcmDataDict.unlock();
  565.     return 0;
  566. }
  567. int checkitem(ostream & out, DcmItem *item,  DcmXfer& oxfer,
  568.         DcmStack& stack, OFBool showFullData, int& dderrors, OFBool verbose)
  569. {
  570.     if (item == NULL) {
  571.         return 0;
  572.     }
  573.     /*
  574.     ** Step through each attribute and check it.
  575.     */
  576.     unsigned long count = item->card();
  577.     for (unsigned long i=0; i<count; i++) {
  578.         DcmElement *elem = item->getElement(i);
  579.         stack.push(elem);
  580.         checkelem(out, elem, oxfer, stack, showFullData, dderrors, verbose);
  581.         stack.pop();
  582.         if (elem->ident() == EVR_SQ) {
  583.             DcmSequenceOfItems *seq = (DcmSequenceOfItems*)elem;
  584.             unsigned long nitems = seq->card();
  585.             for (unsigned long j=0; j<nitems; j++) {
  586.                 /* check each item.  an item is just another dataset */
  587.                 stack.push(seq);
  588.                 checkitem(out, seq->getItem(j), oxfer, stack, showFullData, dderrors, verbose);
  589.                 stack.pop();
  590.             }
  591.         }
  592.     }
  593.     return 0;
  594. }
  595. int dcmchk(
  596.   ostream & out,
  597.   const char* ifname,
  598.   OFBool isDataset,
  599.   E_TransferSyntax xfer,
  600.   OFBool showFullData,
  601.   OFBool loadAllDataInMemory,
  602.   int& dderrors,
  603.   OFBool verbose)
  604. {
  605.     DcmFileFormat *ds = new DcmFileFormat();
  606.     OFCondition cond = ds->loadFile(ifname, xfer, EGL_noChange, DCM_MaxReadLength, isDataset);
  607.     if (! cond.good())
  608.     {
  609.       out << "Error: " << cond.text() << " reading file: " << ifname << endl;
  610.     }
  611.     if (loadAllDataInMemory) {
  612.         ds->loadAllDataIntoMemory();
  613.         if (ds->error() != EC_Normal)
  614.         {
  615.            out << "Error: " << ds->error().text()
  616.                 << " reading file: " << ifname << endl;
  617.             return 1;
  618.         }
  619.     }
  620.     DcmStack stack;
  621.     DcmXfer oxfer(META_HEADER_DEFAULT_TRANSFERSYNTAX);
  622.     DcmMetaInfo *mi = ds->getMetaInfo();
  623.     if (mi->card() > 0)
  624.     {
  625.       // we only check the meta-header if there is something to check
  626.       checkitem(out, mi, oxfer, stack, showFullData, dderrors, verbose);
  627.     }
  628.     oxfer = ds->getDataset()->getOriginalXfer();
  629.     checkitem(out, ds->getDataset(),  oxfer, stack, showFullData, dderrors, verbose);
  630.     delete ds;
  631.     return 0;
  632. }
  633. //*********************************************************
  634. static void
  635. printAttribute(ostream& out, DcmItem* dset,
  636.                const DcmTagKey& key)
  637. {
  638.     DcmElement *elem = NULL;
  639.     DcmStack stack;
  640.     OFCondition ec = EC_Normal;
  641.     ec = dset->search(key, stack, ESM_fromHere, OFFalse);
  642.     elem = (DcmElement*) stack.top();
  643.     elem->print(out, DCMTypes::PF_shortenLongTagValues);
  644. }
  645. static OFBool
  646. chkType1AttributeExistance(ostream& out, DcmItem* dset,
  647.                       const DcmTagKey& key)
  648. {
  649.     OFBool found = OFTrue;
  650.     if (!dset->tagExistsWithValue(key)) {
  651.         DcmTag t(key);
  652.         out << MSGe_missingAtt << endl
  653.             << "   Affected attribute: " << t.getXTag()
  654.             << " " << t.getTagName() << endl;
  655.         out << endl;
  656.         found = OFFalse;
  657.     }
  658.     return found;
  659. }
  660. int dcmchkMetaHeader(ostream& out, DcmMetaInfo* meta, DcmDataset* dset)
  661. {
  662.     if (meta == NULL || meta->card() == 0) {
  663.         /* no meta-header so no errors */
  664.         return 0;
  665.     }
  666.     int nErrs = 0;
  667.     /*
  668.     ** The meta-header should use the LittleEndianExplicit transfer syntax
  669.     */
  670.     if (meta->getOriginalXfer() != EXS_LittleEndianExplicit) {
  671.         DcmXfer used(meta->getOriginalXfer());
  672.         DcmXfer expected(EXS_LittleEndianExplicit);
  673.         out << MSGe_mhxferError << endl
  674.             << "    Expected: " << expected.getXferName() << endl
  675.             << "    Used:     " << used.getXferName() << endl << endl;
  676.         nErrs++;
  677.     }
  678.     /*
  679.     ** Check the meta-header contents
  680.     */
  681.     // examine the FileMetaInformationVersion
  682.     DcmTagKey fmiv(DCM_FileMetaInformationVersion);
  683.     if (chkType1AttributeExistance(out, meta, fmiv)) {
  684.         Uint8 b0 = 0xff;
  685.         Uint8 b1 = 0xff;
  686.         // get bytes
  687.         meta->findAndGetUint8(fmiv, b0, 0);
  688.         meta->findAndGetUint8(fmiv, b1, 1);
  689.         // we expect 0x00/0x01 for the version
  690.         if ((b0 != 0x00) || (b1 != 0x01)) {
  691.             out << MSGe_wrongAtt << endl
  692.                 << "   Invalid FileMetaInformationVersion (expected: 00\01)"
  693.                 << endl << "   Affected attribute: " << endl << "      ";
  694.             printAttribute(out, meta, fmiv);
  695.             out << endl;
  696.             nErrs++;
  697.         }
  698.     } else {
  699.         nErrs++;
  700.     }
  701.     // examine the MediaStorageSOPClassUID
  702.     DcmTagKey msscuid(DCM_MediaStorageSOPClassUID);
  703.     if (chkType1AttributeExistance(out, meta, msscuid)) {
  704.         OFString metaHeaderClassUID;
  705.         meta->findAndGetOFStringArray(msscuid, metaHeaderClassUID);
  706.         // should be the same as SOPClassUID in the dataset
  707.         if (dset && dset->tagExistsWithValue(DCM_SOPClassUID)) {
  708.             OFString datasetClassUID;
  709.             dset->findAndGetOFStringArray(DCM_SOPClassUID, datasetClassUID);
  710.             if (metaHeaderClassUID != datasetClassUID) {
  711.                 out << MSGe_wrongAtt << endl
  712.                     << "   Inconsistant SOP class information"
  713.                 << endl << "   Affected attributes: " << endl << "      ";
  714.                 printAttribute(out, meta, msscuid);
  715.                 out << "      ";
  716.                 printAttribute(out, dset, DCM_SOPClassUID);
  717.                 out << endl;
  718.                 nErrs++;
  719.             }
  720.         }
  721.         if (!dcmFindNameOfUID(metaHeaderClassUID.c_str())) {
  722.             out << MSGe_wrongAtt << endl
  723.                 << "   Unknown SOP Class"
  724.                 << endl << "   Affected attribute: " << endl << "      ";
  725.             printAttribute(out, meta, msscuid);
  726.             out << endl;
  727.             nErrs++;
  728.         }
  729.     } else {
  730.         nErrs++;
  731.     }
  732.     // Examine MediaStorageSOPInstanceUID
  733.     DcmTagKey mssiuid(DCM_MediaStorageSOPInstanceUID);
  734.     if (chkType1AttributeExistance(out, meta, mssiuid)) {
  735.         OFString metaHeaderInstanceUID;
  736.         meta->findAndGetOFStringArray(mssiuid, metaHeaderInstanceUID);
  737.         // should be the same as SOPInstanceUID in the dataset
  738.         if (dset && dset->tagExistsWithValue(DCM_SOPInstanceUID)) {
  739.             OFString datasetInstanceUID;
  740.             dset->findAndGetOFStringArray(DCM_SOPInstanceUID, datasetInstanceUID);
  741.             if (metaHeaderInstanceUID != datasetInstanceUID) {
  742.                 out << MSGe_wrongAtt << endl
  743.                     << "   Inconsistant SOP instance information"
  744.                 << endl << "   Affected attributes: " << endl << "      ";
  745.                 printAttribute(out, meta, mssiuid);
  746.                 out << "      ";
  747.                 printAttribute(out, dset, DCM_SOPInstanceUID);
  748.                 out << endl;
  749.                 nErrs++;
  750.             }
  751.         }
  752.     } else {
  753.         nErrs++;
  754.     }
  755.     // examine the TransferSyntaxUID
  756.     DcmTagKey tsuid(DCM_TransferSyntaxUID);
  757.     if (chkType1AttributeExistance(out, meta, tsuid)) {
  758.         OFString transferSyntaxUID;
  759.         meta->findAndGetOFStringArray(tsuid, transferSyntaxUID);
  760.         // is this transfer syntax known ?
  761.         DcmXfer expected(transferSyntaxUID.c_str());
  762.         if (expected.getXfer() == EXS_Unknown) {
  763.             out << MSGe_wrongAtt << endl
  764.                 << "   Unknown Transfer Syntax"
  765.                 << endl << "   Affected attribute: " << endl << "      ";
  766.             printAttribute(out, meta, tsuid);
  767.             out << endl;
  768.             nErrs++;
  769.         }
  770.         // should be the same as transfer syntax used to read the dataset
  771.         if (dset && (dset->getOriginalXfer() != EXS_LittleEndianExplicit)) {
  772.             DcmXfer used(dset->getOriginalXfer());
  773.             OFString usedTransferSyntaxUID(used.getXferID());
  774.             if (transferSyntaxUID != usedTransferSyntaxUID) {
  775.                 out << MSGe_wrongAtt << endl
  776.                     << "   Dataset not encoded using specified transfer syntax"
  777.                     << endl << "   Affected attribute: " << endl << "      ";
  778.                 printAttribute(out, meta, tsuid);
  779.                 out << "   Dataset encoded using: " << used.getXferName()
  780.                     << endl << endl;
  781.                 nErrs++;
  782.             }
  783.         }
  784.     } else {
  785.         nErrs++;
  786.     }
  787.     // Check the group length information
  788.     DcmTagKey gltag(DCM_MetaElementGroupLength);
  789.     if (chkType1AttributeExistance(out, meta, gltag)) {
  790.         Uint32 len = 0;
  791.         meta->findAndGetUint32(gltag, len, 0);
  792.         // Compute how large the Meta-Header should be
  793.         Uint32 expectedLength = meta->getLength(EXS_LittleEndianExplicit,
  794.                                                 EET_ExplicitLength);
  795.         expectedLength -= 12; // less length of group length element itself
  796.         if (len != expectedLength) {
  797.             out << MSGe_wrongAtt << endl
  798.                 << "   Invalid meta-header group length (expected: "
  799.                 <<  expectedLength  << ")"
  800.                 << endl << "   Affected attribute: " << endl << "      ";
  801.             printAttribute(out, meta, gltag);
  802.             out << endl;
  803.             nErrs++;
  804.         }
  805.     } else {
  806.         nErrs++;
  807.     }
  808.     return nErrs;
  809. }
  810. int checkfile(const char *filename, OFBool verbose, ostream& out, OFConsole *outconsole, OFBool opt_debug)
  811. {
  812.     DcmFileFormat *dfile = new DcmFileFormat();
  813.     if (dfile == NULL)
  814.     {
  815.       out << "Error: out of memory." << endl;
  816.       return -1;
  817.     }
  818.     OFCondition cond = dfile->loadFile(filename);
  819.     if (! cond.good())
  820.     {
  821.       out << "Error: " << cond.text() << " reading file: " << filename << endl;
  822.       delete dfile;
  823.       return -1;
  824.     }
  825.     int numberOfErrors = 0;
  826.     OFBool test_passed = OFTrue;
  827.     DcmDataset *DataSet = dfile->getDataset();
  828.     DcmMetaInfo *MetaInfo = dfile->getMetaInfo();
  829.     if (verbose)
  830.     {
  831.       out << "=========================================================" << endl;
  832.     }
  833.     out << "Testing: " << filename << endl;
  834.     if (verbose)
  835.     {
  836.       out << "=========================================================" << endl << endl;
  837.     } else out << endl;
  838.     if (MetaInfo)
  839.     {
  840.       if (verbose)
  841.       {
  842.         out << "---------------------------------------------------------" << endl
  843.             << "Pass 1 - Inconsistencies between Meta-header and Data Set" << endl
  844.             << "---------------------------------------------------------" << endl << endl;
  845.       }
  846.       numberOfErrors += dcmchkMetaHeader(out, MetaInfo, DataSet);
  847.     }
  848.     if (verbose)
  849.     {
  850.       out << "-------------------------------------------------------------" << endl
  851.           << "Pass 2 - Inconsistencies between Data Dictionary and Data Set" << endl
  852.           << "-------------------------------------------------------------" << endl << endl;
  853.     }
  854.     dcmchk(out, opt_filename, OFFalse, EXS_Unknown,
  855.           OFFalse /* showFullData */, OFTrue /* loadAllDataInMemory */,
  856.           numberOfErrors, verbose);
  857.     if (verbose)
  858.     {
  859.       out << "-------------------------------------------------------------" << endl
  860.           << "Pass 3 - Semantic Check of Presentation State Object         " << endl
  861.           << "-------------------------------------------------------------" << endl << endl;
  862.     }
  863.     DcmUniqueIdentifier sopclassuid(DCM_SOPClassUID);
  864.     DcmStack stack;
  865.     if (EC_Normal == DataSet->search(DCM_SOPClassUID, stack, ESM_fromHere, OFFalse))
  866.     {
  867.       sopclassuid = *((DcmUniqueIdentifier *)(stack.top()));
  868.     }
  869.     OFString aString;
  870.     sopclassuid.getOFString(aString,0);
  871.     if (aString == UID_GrayscaleSoftcopyPresentationStateStorage)
  872.     {
  873.       DcmPresentationState pState;
  874.       pState.setLog(outconsole, OFTrue, opt_debug);
  875.       if (pState.read(*DataSet).bad())
  876.       {
  877.         test_passed = OFFalse;
  878.         out << endl;
  879.       }
  880.     } else {
  881.       if (verbose) out << "Not a Grayscale Softcopy Presentation State, skipping pass 3." << endl <<endl;
  882.     }
  883.     if (numberOfErrors > 0) test_passed = OFFalse;
  884.     if (test_passed) out << "Test passed." << endl << endl;
  885.     else out << "Test failed - one or more errors." << endl << endl;
  886.     if (dfile) delete dfile;
  887.     return numberOfErrors;
  888. }
  889. void closeLog()
  890. {
  891.   ofConsole.setCout();
  892.   ofConsole.split();
  893.   if (logstream != &COUT)
  894.   {
  895.     delete logstream;
  896.     logstream = &COUT;
  897.   }
  898. }
  899. #define SHORTCOL 2
  900. #define LONGCOL 14
  901. int main(int argc, char *argv[])
  902. {
  903. #ifdef HAVE_GUSI_H
  904.     GUSISetup(GUSIwithSIOUXSockets);
  905.     GUSISetup(GUSIwithInternetSockets);
  906. #endif
  907. #ifdef HAVE_WINSOCK_H
  908.     WSAData winSockData;
  909.     /* we need at least version 1.1 */
  910.     WORD winSockVersionNeeded = MAKEWORD( 1, 1 );
  911.     WSAStartup(winSockVersionNeeded, &winSockData);
  912. #endif
  913.     SetDebugLevel((0));
  914.     OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION , "Checking tool for presentation states", rcsid);
  915.     OFCommandLine cmd;
  916.     cmd.setOptionColumns(LONGCOL, SHORTCOL);
  917.     cmd.setParamColumn(LONGCOL + SHORTCOL + 2);
  918.     cmd.addParam("filename_in",   "presentation state file(s) to be checked", OFCmdParam::PM_MultiMandatory);
  919.     cmd.addGroup("general options:");
  920.      cmd.addOption("--help",        "-h",    "print this help text and exit");
  921.      cmd.addOption("--version",              "print version information and exit", OFTrue /* exclusive */);
  922.      cmd.addOption("--verbose",     "-v",    "verbose mode, print actions");
  923.      cmd.addOption("--debug",       "-d",    "debug mode, print debug information");
  924.      cmd.addOption("--logfile",     "-l", 1, "[f]ilename: string",
  925.                                              "write output to logfile f");
  926.     /* evaluate command line */
  927.     prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION);
  928.     if (app.parseCommandLine(cmd, argc, argv, OFCommandLine::ExpandWildcards))
  929.     {
  930.       /* check exclusive options first */
  931.       if (cmd.getParamCount() == 0)
  932.       {
  933.         if (cmd.findOption("--version"))
  934.         {
  935.             app.printHeader(OFTrue /*print host identifier*/);          // uses ofConsole.lockCerr()
  936.             CERR << endl << "External libraries used:";
  937. #ifdef WITH_ZLIB
  938.             CERR << endl << "- ZLIB, Version " << zlibVersion() << endl;
  939. #else
  940.             CERR << " none" << endl;
  941. #endif
  942.             return 0;
  943.          }
  944.       }
  945.       /* options */
  946.       if (cmd.findOption("--verbose")) opt_verbose = OFTrue;
  947.       if (cmd.findOption("--debug"))   opt_debugMode = 3;
  948.       if (cmd.findOption("--logfile"))
  949.       {
  950.         app.checkValue(cmd.getValue(opt_logfilename));
  951.       }
  952.     }
  953.     SetDebugLevel((opt_debugMode));
  954.     if (opt_logfilename)
  955.     {
  956.       ofstream *newstream = new ofstream(opt_logfilename);
  957.       if (newstream && (newstream->good()))
  958.       {
  959.         logstream=newstream;
  960.         ofConsole.setCout(logstream);
  961.         ofConsole.join();
  962.       }
  963.       else
  964.       {
  965.        delete newstream;
  966.       }
  967.     }
  968.     int paramCount = cmd.getParamCount();
  969.     for (int param=1; param <= paramCount; param++)
  970.     {
  971.       cmd.getParam(param, opt_filename);
  972.       checkfile(opt_filename, opt_verbose, *logstream, &ofConsole, ((opt_debugMode>0) ? OFTrue : OFFalse));
  973.     }
  974.     closeLog();
  975. #ifdef DEBUG
  976.     dcmDataDict.clear();  /* useful for debugging with dmalloc */
  977. #endif
  978.     return 0;
  979. }
  980. /*
  981.  * CVS/RCS Log:
  982.  * $Log: dcmpschk.cc,v $
  983.  * Revision 1.16  2004/02/04 15:44:38  joergr
  984.  * Removed acknowledgements with e-mail addresses from CVS log.
  985.  *
  986.  * Revision 1.15  2003/09/05 09:00:49  meichel
  987.  * Updated presentation state checker to use class DcmPresentationState
  988.  *   instead of DVPresentationState. Imported updated VR checking code
  989.  *   from module dcmcheck.
  990.  *
  991.  * Revision 1.14  2003/09/01 12:58:58  wilkens
  992.  * Added #include to file to be able to compile again under Win32.
  993.  *
  994.  * Revision 1.13  2003/03/12 17:34:20  meichel
  995.  * Updated DcmObject::print() flags
  996.  *
  997.  * Revision 1.12  2002/11/27 15:47:52  meichel
  998.  * Adapted module dcmpstat to use of new header file ofstdinc.h
  999.  *
  1000.  * Revision 1.11  2002/11/26 08:44:27  meichel
  1001.  * Replaced all includes for "zlib.h" with <zlib.h>
  1002.  *   to avoid inclusion of zlib.h in the makefile dependencies.
  1003.  *
  1004.  * Revision 1.10  2002/09/23 18:26:07  joergr
  1005.  * Added new command line option "--version" which prints the name and version
  1006.  * number of external libraries used (incl. preparation for future support of
  1007.  * 'config.guess' host identifiers).
  1008.  *
  1009.  * Revision 1.9  2002/08/20 12:21:53  meichel
  1010.  * Adapted code to new loadFile and saveFile methods, thus removing direct
  1011.  *   use of the DICOM stream classes.
  1012.  *
  1013.  * Revision 1.8  2002/06/14 10:44:18  meichel
  1014.  * Adapted log file handling to ofConsole singleton
  1015.  *
  1016.  * Revision 1.7  2002/05/02 14:10:04  joergr
  1017.  * Added support for standard and non-standard string streams (which one is
  1018.  * supported is detected automatically via the configure mechanism).
  1019.  *
  1020.  * Revision 1.6  2002/04/16 14:01:27  joergr
  1021.  * Added configurable support for C++ ANSI standard includes (e.g. streams).
  1022.  *
  1023.  * Revision 1.5  2001/11/09 16:04:51  joergr
  1024.  * Changed type of variable to avoid compiler warnings (comparison of signed
  1025.  * and unsigned data).
  1026.  *
  1027.  * Revision 1.4  2001/10/02 11:51:59  joergr
  1028.  * Introduced new general purpose functions to get/put DICOM element values
  1029.  * from/to an item/dataset - removed some old and rarely used functions.
  1030.  *
  1031.  * Revision 1.3  2001/09/26 15:36:02  meichel
  1032.  * Adapted dcmpstat to class OFCondition
  1033.  *
  1034.  * Revision 1.2  2001/06/01 15:50:08  meichel
  1035.  * Updated copyright header
  1036.  *
  1037.  * Revision 1.1  2000/06/21 15:40:32  meichel
  1038.  * Added initial version of Presentation State Checker.
  1039.  *
  1040.  *
  1041.  */