1 /*
2  *
3  *  Copyright (C) 1994-2019, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module:  dcmdata
15  *
16  *  Author:  Gerd Ehlers, Andreas Barth
17  *
18  *  Purpose: Implementation of class DcmDirectoryRecord
19  *
20  */
21 
22 
23 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24 
25 #define INCLUDE_CSTDLIB
26 #define INCLUDE_CSTDIO
27 #define INCLUDE_CSTRING
28 #define INCLUDE_CCTYPE
29 #define INCLUDE_LIBC
30 #define INCLUDE_UNISTD
31 #include "dcmtk/ofstd/ofstdinc.h"
32 
33 #include "dcmtk/ofstd/ofstream.h"
34 #include "dcmtk/ofstd/ofcast.h"
35 #include "dcmtk/ofstd/ofstd.h"
36 
37 #include "dcmtk/dcmdata/dcdirrec.h"
38 #include "dcmtk/dcmdata/dctk.h"
39 #include "dcmtk/dcmdata/dcxfer.h"
40 #include "dcmtk/dcmdata/dcvr.h"
41 #include "dcmtk/dcmdata/dcvrus.h"
42 #include "dcmtk/dcmdata/dcspchrs.h"
43 
44 #ifdef HAVE_UNIX_H
45 #if defined(macintosh) && defined (HAVE_WINSOCK_H)
46 /* unix.h defines timeval incompatible with winsock.h */
47 #define timeval _UNWANTED_timeval
48 #endif
49 #include <unix.h>       /* for unlink() under Metrowerks C++ (Macintosh) */
50 #undef timeval
51 #endif
52 
53 
54 // ********************************
55 
56 
57 static const char *DRTypeNames[] =
58 {
59     "root",
60     "CURVE",
61     "FILM BOX",
62     "FILM SESSION",
63     "IMAGE",
64     "IMAGE BOX",
65     "INTERPRETATION",
66     "MODALITY LUT",
67     "MRDR",
68     "OVERLAY",
69     "PATIENT",
70     "PRINT QUEUE",
71     "PRIVATE",
72     "RESULTS",
73     "SERIES",
74     "STUDY",
75     "STUDY COMPONENT",
76     "TOPIC",
77     "VISIT",
78     "VOI LUT",
79     "SR DOCUMENT", /* was "STRUCT REPORT" in old frozen draft */
80     "PRESENTATION",
81     "WAVEFORM",
82     "RT DOSE",
83     "RT STRUCTURE SET",
84     "RT PLAN",
85     "RT TREAT RECORD",
86     "STORED PRINT",
87     "KEY OBJECT DOC",
88     "REGISTRATION",
89     "FIDUCIAL",
90     "RAW DATA",
91     "SPECTROSCOPY",
92     "ENCAP DOC",
93     "VALUE MAP",
94     "HANGING PROTOCOL",
95     "STEREOMETRIC",
96     "HL7 STRUC DOC",
97     "PALETTE",
98     "SURFACE",
99     "MEASUREMENT",
100     "IMPLANT",
101     "IMPLANT GROUP",
102     "IMPLANT ASSY",
103     "PLAN",
104     "SURFACE SCAN",
105     "TRACT",
106     "ASSESSMENT",
107     "RADIOTHERAPY"
108 };
109 
110 static const short DIM_OF_DRTypeNames = OFstatic_cast(short, (sizeof(DRTypeNames) / sizeof(DRTypeNames[0])));
111 
112 
113 // ********************************
114 
115 
DcmDirectoryRecord()116 DcmDirectoryRecord::DcmDirectoryRecord()
117   : DcmItem(DCM_ItemTag),
118     recordsOriginFile(),
119     lowerLevelList(new DcmSequenceOfItems(DCM_DirectoryRecordSequence)),
120     DirRecordType(ERT_Private),
121     referencedMRDR(NULL),
122     numberOfReferences(0),
123     offsetInFile(0)
124 {
125 }
126 
127 
128 // ********************************
129 
130 
DcmDirectoryRecord(const DcmTag & tag,const Uint32 len)131 DcmDirectoryRecord::DcmDirectoryRecord(const DcmTag &tag,
132                                        const Uint32 len)
133   : DcmItem(tag, len),
134     recordsOriginFile(),
135     lowerLevelList(new DcmSequenceOfItems(DCM_DirectoryRecordSequence)),
136     DirRecordType(ERT_Private),
137     referencedMRDR(NULL),
138     numberOfReferences(0),
139     offsetInFile(0)
140 {
141 }
142 
143 
144 // ********************************
145 
146 
DcmDirectoryRecord(const E_DirRecType recordType,const char * referencedFileID,const OFFilename & sourceFilename,DcmFileFormat * fileFormat)147 DcmDirectoryRecord::DcmDirectoryRecord(const E_DirRecType recordType,
148                                        const char *referencedFileID,
149                                        const OFFilename &sourceFilename,
150                                        DcmFileFormat *fileFormat)
151   : DcmItem(DCM_ItemTag),
152     recordsOriginFile(),
153     lowerLevelList(new DcmSequenceOfItems(DCM_DirectoryRecordSequence)),
154     DirRecordType(recordType),
155     referencedMRDR(NULL),
156     numberOfReferences(0),
157     offsetInFile(0)
158 {
159     setRecordsOriginFile(sourceFilename);
160 
161     if (DirRecordType != ERT_root)
162         errorFlag = fillElementsAndReadSOP(referencedFileID, sourceFilename, fileFormat);
163 }
164 
165 
166 // ********************************
167 
168 
DcmDirectoryRecord(const char * recordTypeName,const char * referencedFileID,const OFFilename & sourceFilename,DcmFileFormat * fileFormat)169 DcmDirectoryRecord::DcmDirectoryRecord(const char *recordTypeName,
170                                        const char *referencedFileID,
171                                        const OFFilename &sourceFilename,
172                                        DcmFileFormat *fileFormat)
173   : DcmItem(DCM_ItemTag),
174     recordsOriginFile(),
175     lowerLevelList(new DcmSequenceOfItems(DCM_DirectoryRecordSequence)),
176     DirRecordType(ERT_Private),
177     referencedMRDR(NULL),
178     numberOfReferences(0),
179     offsetInFile(0)
180 {
181     DirRecordType = recordNameToType(recordTypeName);
182     setRecordsOriginFile(sourceFilename);
183 
184     if (DirRecordType != ERT_root)
185         errorFlag = fillElementsAndReadSOP(referencedFileID, sourceFilename, fileFormat);
186 }
187 
188 
189 // ********************************
190 
191 
DcmDirectoryRecord(const DcmDirectoryRecord & old)192 DcmDirectoryRecord::DcmDirectoryRecord(const DcmDirectoryRecord &old)
193   : DcmItem(old),
194     recordsOriginFile(old.recordsOriginFile),
195     lowerLevelList(new DcmSequenceOfItems(*old.lowerLevelList)),
196     DirRecordType(old.DirRecordType),
197     referencedMRDR(old.referencedMRDR),  // copies a pointer!
198     numberOfReferences(old.numberOfReferences),
199     offsetInFile(old.offsetInFile)
200 {
201 }
202 
203 
204 // ********************************
205 
206 
operator =(const DcmDirectoryRecord & obj)207 DcmDirectoryRecord& DcmDirectoryRecord::operator=(const DcmDirectoryRecord& obj)
208 {
209     if (this != &obj)
210     {
211         // copy parent's member variables
212         DcmItem::operator=(obj);
213         // copy DcmDirectoryRecords' member variables
214         recordsOriginFile = obj.recordsOriginFile;
215         lowerLevelList = new DcmSequenceOfItems(*obj.lowerLevelList);
216         DirRecordType = obj.DirRecordType;
217         referencedMRDR = obj.referencedMRDR;  // copies a pointer!
218         numberOfReferences = obj.numberOfReferences;
219         offsetInFile = obj.offsetInFile;
220     }
221     return *this;
222 }
223 
224 
225 // ********************************
226 
227 
copyFrom(const DcmObject & rhs)228 OFCondition DcmDirectoryRecord::copyFrom(const DcmObject& rhs)
229 {
230     if (this != &rhs)
231     {
232         if (rhs.ident() != ident()) return EC_IllegalCall;
233         *this = OFstatic_cast(const DcmDirectoryRecord &, rhs);
234     }
235     return EC_Normal;
236 }
237 
238 
239 // ********************************
240 
241 
~DcmDirectoryRecord()242 DcmDirectoryRecord::~DcmDirectoryRecord()
243 {
244     delete lowerLevelList;
245 }
246 
247 
248 // ********************************
249 
250 
recordNameToType(const char * recordTypeName)251 E_DirRecType DcmDirectoryRecord::recordNameToType(const char *recordTypeName)
252 {
253     E_DirRecType recType = ERT_Private;
254     if (recordTypeName != NULL)
255     {
256         short i = 0;
257         while (i < DIM_OF_DRTypeNames && strcmp(DRTypeNames[i], recordTypeName) != 0)
258             i++;
259 
260         if (i < DIM_OF_DRTypeNames && strcmp(DRTypeNames[i], recordTypeName) == 0)
261             recType = OFstatic_cast(E_DirRecType, i);
262         else if (strcmp(recordTypeName,"STRUCT REPORT") == 0)
263             recType = ERT_SRDocument; // we recognize the old name as well
264         DCMDATA_TRACE("DcmDirectoryRecord::recordNameToType() input char*=\"" << recordTypeName
265             << "\" output enum=" << recType);
266     }
267     return recType;
268 }
269 
270 
271 // ********************************
272 
273 
buildFileName(const char * origName,char * destName,size_t len) const274 char *DcmDirectoryRecord::buildFileName(const char *origName,
275                                         char *destName,
276                                         size_t len) const
277 {
278     const char *from = origName;
279     char *to = destName;
280     char c;
281     char lastchar = '\0';
282     while ((c = *from++) != 0)
283     {
284         if (c == '\\')
285         {
286             if (lastchar != '\\')      // eliminate double '\\'
287                 *to++ = PATH_SEPARATOR;
288         } else {
289             *to++ = c;
290         }
291         lastchar = c;
292     }
293     *to = '\0';
294 
295     /*
296     ** Some OS's append a '.' to the filename of a ISO9660 filesystem.
297     ** If the filename does not exist then try appending a '.'
298     */
299     FILE* f = NULL;
300     if ((f = fopen(destName, "rb")) != NULL)
301     {
302         fclose(f);
303     } else {
304         size_t buflen = strlen(destName) + 2;
305         char* newname = new char[buflen];
306         OFStandard::strlcpy(newname, destName, buflen);
307         OFStandard::strlcat(newname, ".", buflen);
308         if ((f = fopen(newname, "rb")) != NULL)
309         {
310             fclose(f);
311             OFStandard::strlcpy(destName, newname, len);
312         } else {
313             /* we cannot find the file. let the caller deal with this */
314         }
315         delete[] newname;
316     }
317     return destName;
318 }
319 
320 
321 // ********************************
322 
323 
checkHierarchy(const E_DirRecType upperRecord,const E_DirRecType lowerRecord)324 OFCondition DcmDirectoryRecord::checkHierarchy(const E_DirRecType upperRecord,
325                                                const E_DirRecType lowerRecord)
326 {
327     OFCondition l_error = EC_IllegalCall;
328     switch (upperRecord)
329     {
330         case ERT_root:
331             switch (lowerRecord)
332             {
333                 case ERT_Patient:
334                 case ERT_PrintQueue:
335                 case ERT_Topic:
336                 case ERT_HangingProtocol:
337                 case ERT_Palette:
338                 case ERT_Implant:
339                 case ERT_ImplantGroup:
340                 case ERT_ImplantAssy:
341                 case ERT_Private:
342                     l_error = EC_Normal;
343                     break;
344                 default:
345                     l_error = EC_IllegalCall;
346                     break;
347             }
348             break;
349         case ERT_FilmBox:  // retired
350             switch (lowerRecord)
351             {
352                 case ERT_ImageBox:
353                 case ERT_Private:
354                     l_error = EC_Normal;
355                     break;
356                 default:
357                     l_error = EC_IllegalCall;
358                     break;
359             }
360             break;
361         case ERT_FilmSession:  // retired
362             switch (lowerRecord)
363             {
364                 case ERT_FilmBox:
365                 case ERT_Private:
366                     l_error = EC_Normal;
367                     break;
368                 default:
369                     l_error = EC_IllegalCall;
370                     break;
371             }
372             break;
373         case ERT_Patient:
374             switch (lowerRecord)
375             {
376                 case ERT_Study:
377                 case ERT_HL7StrucDoc:  // retired
378                 case ERT_Private:
379                     l_error = EC_Normal;
380                     break;
381                 default:
382                     l_error = EC_IllegalCall;
383                     break;
384             }
385             break;
386         case ERT_PrintQueue:  // retired
387             switch (lowerRecord)
388             {
389                 case ERT_FilmSession:
390                 case ERT_Private:
391                     l_error = EC_Normal;
392                     break;
393                 default:
394                     l_error = EC_IllegalCall;
395                     break;
396             }
397             break;
398         case ERT_Results:  // retired
399             switch (lowerRecord)
400             {
401                 case ERT_Interpretation:
402                 case ERT_Private:
403                     l_error = EC_Normal;
404                     break;
405                 default:
406                     l_error = EC_IllegalCall;
407                     break;
408             }
409             break;
410         case ERT_Series:
411             switch (lowerRecord)
412             {
413                 case ERT_Curve:
414                 case ERT_Image:
415                 case ERT_ModalityLut:
416                 case ERT_Overlay:
417                 case ERT_VoiLut:
418                 case ERT_SRDocument:
419                 case ERT_Presentation:
420                 case ERT_Waveform:
421                 case ERT_RTDose:
422                 case ERT_RTStructureSet:
423                 case ERT_RTPlan:
424                 case ERT_RTTreatRecord:
425                 case ERT_StoredPrint:
426                 case ERT_KeyObjectDoc:
427                 case ERT_Registration:
428                 case ERT_Fiducial:
429                 case ERT_RawData:
430                 case ERT_Spectroscopy:
431                 case ERT_EncapDoc:
432                 case ERT_ValueMap:
433                 case ERT_Stereometric:
434                 case ERT_Surface:
435                 case ERT_Measurement:
436                 case ERT_Plan:
437                 case ERT_SurfaceScan:
438                 case ERT_Tract:
439                 case ERT_Assessment:
440                 case ERT_Radiotherapy:
441                 case ERT_Private:
442                     l_error = EC_Normal;
443                     break;
444                 default:
445                     l_error = EC_IllegalCall;
446                     break;
447             }
448             break;
449         case ERT_Study:
450             switch (lowerRecord)
451             {
452                 case ERT_FilmSession:
453                 case ERT_Results:
454                 case ERT_Series:
455                 case ERT_StudyComponent:
456                 case ERT_Visit:
457                 case ERT_Private:
458                     l_error = EC_Normal;
459                     break;
460                 default:
461                     l_error = EC_IllegalCall;
462                     break;
463             }
464             break;
465         case ERT_Topic:  // retired
466             switch (lowerRecord)
467             {
468                 case ERT_Curve:
469                 case ERT_FilmSession:
470                 case ERT_Image:
471                 case ERT_ModalityLut:
472                 case ERT_Overlay:
473                 case ERT_Series:
474                 case ERT_Study:
475                 case ERT_VoiLut:
476                 case ERT_SRDocument:
477                 case ERT_Presentation:
478                 case ERT_Waveform:
479                 case ERT_RTDose:
480                 case ERT_RTStructureSet:
481                 case ERT_RTPlan:
482                 case ERT_RTTreatRecord:
483                 case ERT_StoredPrint:
484                 case ERT_KeyObjectDoc:
485                 case ERT_Registration:
486                 case ERT_Fiducial:
487                 case ERT_RawData:
488                 case ERT_Spectroscopy:
489                 case ERT_Private:
490                     l_error = EC_Normal;
491                     break;
492                 default:
493                     l_error = EC_IllegalCall;
494                     break;
495             }
496             break;
497         case ERT_Mrdr:  // retired
498             l_error = EC_IllegalCall;
499             break;
500         case ERT_Curve:
501         case ERT_Image:
502         case ERT_ImageBox:
503         case ERT_Interpretation:
504         case ERT_ModalityLut:
505         case ERT_Overlay:
506         case ERT_StudyComponent:
507         case ERT_Visit:
508         case ERT_VoiLut:
509         case ERT_SRDocument:
510         case ERT_Presentation:
511         case ERT_Waveform:
512         case ERT_RTDose:
513         case ERT_RTStructureSet:
514         case ERT_RTPlan:
515         case ERT_RTTreatRecord:
516         case ERT_StoredPrint:
517         case ERT_KeyObjectDoc:
518         case ERT_Registration:
519         case ERT_Fiducial:
520         case ERT_RawData:
521         case ERT_Spectroscopy:
522         case ERT_EncapDoc:
523         case ERT_ValueMap:
524         case ERT_HangingProtocol:
525         case ERT_Stereometric:
526         case ERT_HL7StrucDoc:  // retired
527         case ERT_Palette:
528         case ERT_Surface:
529         case ERT_Measurement:
530         case ERT_Implant:
531         case ERT_ImplantGroup:
532         case ERT_ImplantAssy:
533         case ERT_Plan:
534         case ERT_SurfaceScan:
535         case ERT_Tract:
536         case ERT_Assessment:
537         case ERT_Radiotherapy:
538         case ERT_Private:
539             switch (lowerRecord)
540             {
541                 case ERT_Private:
542                     l_error = EC_Normal;
543                     break;
544                 default:
545                     l_error = EC_IllegalCall;
546                     break;
547             }
548             break;
549         default:
550             l_error = EC_Normal;
551             break;
552     }
553     return l_error;
554 }
555 
556 
557 // ********************************
558 
559 
setRecordType(E_DirRecType newType)560 OFCondition DcmDirectoryRecord::setRecordType(E_DirRecType newType)
561 {
562     OFCondition l_error = EC_Normal;
563 
564     DcmTag dirRecTag(DCM_DirectoryRecordType);
565     DcmCodeString *csP = new DcmCodeString(dirRecTag);
566     csP->putString(DRTypeNames[newType]);
567     insert(csP, OFTrue);
568 
569     return l_error;
570 }
571 
572 
573 // ********************************
574 
575 
576 
lookForRecordType()577 E_DirRecType DcmDirectoryRecord::lookForRecordType()
578 {
579     E_DirRecType localType = ERT_Private;
580     if (!elementList->empty())
581     {
582         DcmStack stack;
583         if (search(DCM_DirectoryRecordType, stack, ESM_fromHere, OFFalse).good())
584         {
585             if (stack.top()->ident() == EVR_CS)
586             {
587                 char *recName = NULL;
588                 DcmCodeString *recType = OFstatic_cast(DcmCodeString *, stack.top());
589                 recType->verify(OFTrue);            // force dealignment
590                 recType->getString(recName);
591                 localType = recordNameToType(recName);
592 
593                 DCMDATA_TRACE("DcmDirectoryRecord::lookForRecordType() RecordType Element "
594                     << recType->getTag() << " Type = " << DRTypeNames[DirRecordType]);
595             }
596         }
597     }
598     return localType;
599 }
600 
601 
602 // ********************************
603 
hostToDicomFilename(char * fname)604 static void hostToDicomFilename(char *fname)
605 {
606     /*
607     ** Massage filename into DICOM format.
608     ** Eliminate any invalid characters.
609     ** Most commonly there is a '.' at the end of a filename.
610     */
611     size_t len = strlen(fname);
612     int k = 0;
613     unsigned char c = '\0';
614     for (size_t i = 0; i < len; ++i)
615     {
616         c = OFstatic_cast(unsigned char, fname[i]);
617         /* the PATH_SEPARATOR depends on the OS (see <osconfig.h>) */
618         if (c == PATH_SEPARATOR)
619         {
620             fname[k++] = '\\';
621         } else if (isalpha(c) || isdigit(c) || (c == '_') || (c == '\\')) {
622             /* filenames in DICOM must always be in uppercase */
623             fname[k++] = OFstatic_cast(char, toupper(c));
624         }
625     }
626     fname[k] = '\0';
627 }
628 
629 
setReferencedFileID(const char * referencedFileID)630 OFCondition DcmDirectoryRecord::setReferencedFileID(const char *referencedFileID)
631 {
632     OFCondition l_error = EC_Normal;
633 
634     size_t bufsize = strlen(referencedFileID) + 1;
635     char* newFname = new char[bufsize];
636     OFStandard::strlcpy(newFname, referencedFileID, bufsize);
637     hostToDicomFilename(newFname);
638 
639     DcmTag refFileTag(DCM_ReferencedFileID);
640     DcmCodeString *csP = new DcmCodeString(refFileTag);
641     if (referencedFileID != NULL)
642         csP->putString(newFname);
643     insert(csP, OFTrue);
644 
645     delete[] newFname;
646     return l_error;
647 }
648 
649 
650 // ********************************
651 
652 
lookForReferencedFileID()653 const char *DcmDirectoryRecord::lookForReferencedFileID()
654 {
655     char *localFile = NULL;
656     if (!elementList->empty())
657     {
658         DcmStack stack;
659         if (search(DCM_ReferencedFileID, stack, ESM_fromHere, OFFalse).good())
660         {
661             if (stack.top()->ident() == EVR_CS)
662             {
663                 DcmCodeString *refFile = OFstatic_cast(DcmCodeString *, stack.top());
664                 refFile->verify(OFTrue);          // force dealignment
665                 refFile->getString(localFile);
666                 if (localFile != NULL && *localFile == '\0')
667                     localFile = NULL;
668             }
669         }
670     }
671     DCMDATA_TRACE("DcmDirectoryRecord::lookForReferencedFileID() ReferencedFileID = "
672         << ((localFile) ? localFile : ""));
673 
674     return localFile;
675 }
676 
677 
678 // ********************************
679 
680 
lookForReferencedMRDR()681 DcmDirectoryRecord *DcmDirectoryRecord::lookForReferencedMRDR()
682 {
683     DcmDirectoryRecord *localMRDR = NULL;
684     if (!elementList->empty())
685     {
686         DcmStack stack;
687         if (search(DCM_RETIRED_MRDRDirectoryRecordOffset, stack, ESM_fromHere, OFFalse).good())
688         {
689             if (stack.top()->ident() == EVR_up)
690             {
691                 DcmUnsignedLongOffset *offElem;
692                 offElem = OFstatic_cast(DcmUnsignedLongOffset *, stack.top());
693                 localMRDR = OFstatic_cast(DcmDirectoryRecord *, offElem->getNextRecord());
694 #ifdef DEBUG
695                 Uint32 l_uint = 0;
696                 offElem->getUint32(l_uint);
697                 DCMDATA_TRACE("DcmDirectoryRecord::lookForReferencedMRDR() MRDR Offset Element "
698                     << offElem->getTag() << " offs=0x"
699                     << STD_NAMESPACE hex << STD_NAMESPACE setfill('0')
700                     << STD_NAMESPACE setw(8) << l_uint
701                     << " p=" << OFstatic_cast(void *, offElem)
702                     << " n=" << OFstatic_cast(void *, localMRDR));
703 #endif
704             }
705         }
706     }
707     if (localMRDR == NULL)
708         DCMDATA_TRACE("DcmDirectoryRecord::lookForReferencedMRDR() no ReferencedMRDR found");
709 
710     return localMRDR;
711 }
712 
713 
714 // ********************************
715 
716 
getReferencedFileName()717 const char *DcmDirectoryRecord::getReferencedFileName()
718 {
719     const char *localFile = NULL;
720     if (referencedMRDR != NULL)
721         localFile = referencedMRDR->lookForReferencedFileID();
722     else
723         localFile = lookForReferencedFileID();
724     return localFile;
725 }
726 
727 
728 // ********************************
729 
730 
setRecordInUseFlag(const Uint16 newFlag)731 OFCondition DcmDirectoryRecord::setRecordInUseFlag(const Uint16 newFlag)
732 {
733     OFCondition l_error = EC_Normal;
734 
735     DcmTag recInUseTag(DCM_RecordInUseFlag);
736     DcmUnsignedShort *usP = new DcmUnsignedShort(recInUseTag);
737     usP->putUint16(newFlag);
738     insert(usP, OFTrue);
739 
740     return l_error;
741 }
742 
743 
744 // ********************************
745 
746 
lookForRecordInUseFlag()747 Uint16 DcmDirectoryRecord::lookForRecordInUseFlag()
748 {
749     Uint16 localFlag = Uint16(0xffff);     // default value: record is in use
750     if (!elementList->empty())
751     {
752         DcmStack stack;
753         if (search(DCM_RecordInUseFlag, stack, ESM_fromHere, OFFalse).good())
754         {
755             if (stack.top()->ident() == EVR_US)
756                 errorFlag = OFstatic_cast(DcmUnsignedShort *, stack.top())->getUint16(localFlag);
757         }
758     }
759     return localFlag;
760 }
761 
762 
763 // ********************************
764 
765 
getFileOffset() const766 Uint32 DcmDirectoryRecord::getFileOffset() const
767 {
768     return offsetInFile;
769 }
770 
771 
772 // ********************************
773 
774 
setFileOffset(Uint32 position)775 Uint32 DcmDirectoryRecord::setFileOffset(Uint32 position)
776 {
777     offsetInFile = position;
778     return position;
779 }
780 
781 
782 // ********************************
783 
784 
setNumberOfReferences(Uint32 newRefNum)785 OFCondition DcmDirectoryRecord::setNumberOfReferences(Uint32 newRefNum)
786 {
787     OFCondition l_error = EC_Normal;
788     if (DirRecordType == ERT_Mrdr)
789     {
790         // insert new value
791         DcmTag numRefTag(DCM_RETIRED_NumberOfReferences);
792         DcmUnsignedLong *newUL = new DcmUnsignedLong(numRefTag);
793         newUL->putUint32(newRefNum);
794         insert(newUL, OFTrue);
795     } else {
796         errorFlag = EC_IllegalCall;
797         DCMDATA_ERROR("illegal usage of DcmDirectoryRecord::setNumberOfReferences() - RecordType must be MRDR");
798     }
799     return l_error;
800 }
801 
802 
803 // ********************************
804 
805 
lookForNumberOfReferences()806 Uint32 DcmDirectoryRecord::lookForNumberOfReferences()
807 {
808     Uint32 localRefNum = 0L;
809     if (!elementList->empty())
810     {
811         DcmStack stack;
812         if (search(DCM_RETIRED_NumberOfReferences, stack, ESM_fromHere, OFFalse).good())
813         {
814             if (stack.top()->ident() == EVR_UL)
815                 errorFlag = OFstatic_cast(DcmUnsignedLong *, stack.top())->getUint32(localRefNum);
816         }
817     }
818     return localRefNum;
819 }
820 
821 
822 // ********************************
823 
824 
increaseRefNum()825 Uint32 DcmDirectoryRecord::increaseRefNum()
826 {
827     if (DirRecordType == ERT_Mrdr)
828     {
829         if (numberOfReferences == 0L)
830             setRecordInUseFlag(0xffff);         // activate record
831         numberOfReferences++;
832         errorFlag = setNumberOfReferences(numberOfReferences);
833     } else {
834         errorFlag = EC_IllegalCall;
835         DCMDATA_ERROR("illegal usage of DcmDirectoryRecord::increaseRefNum() - RecordType must be MRDR");
836     }
837     return numberOfReferences;
838 }
839 
840 
841 // ********************************
842 
843 
decreaseRefNum()844 Uint32 DcmDirectoryRecord::decreaseRefNum()
845 {
846     if (DirRecordType == ERT_Mrdr)
847     {
848         if (numberOfReferences > 0)
849         {
850             numberOfReferences--;
851             if (numberOfReferences == 0L)
852                 setRecordInUseFlag(0x0000);     // deactivate record
853             errorFlag = setNumberOfReferences(numberOfReferences);
854         } else {
855             errorFlag = EC_IllegalCall;
856             DCMDATA_WARN("DcmDirectoryRecord::decreaseRefNum() attempt to decrease value lower than zero");
857         }
858     } else {
859         errorFlag = EC_IllegalCall;
860         DCMDATA_ERROR("illegal usage of DcmDirectoryRecord::decreaseRefNum() - RecordType must be MRDR");
861     }
862     return numberOfReferences;
863 }
864 
865 
866 
867 // ********************************
868 
869 // --- IGNORE THE FOLLOWING GERMAN COMMENT ---
870 //
871 // Es werden alle Datenelemente gemaess Tabelle B.3.2.2-1 DICOM part 10
872 // erzeugt.
873 // Wird ein gueltiger Dateiname (referencedFileID) fuer eine SOP-Instanz
874 // uebergeben, so werden alle benoetigten keys aus dieser Datei ausgelesen:
875 //   Das Datenelement MRDRDirectoryRecordOffset entfaellt.
876 //   directFromFile == OFTrue
877 //
878 // Ist referencedFileID ein NULL-Zeiger, so wird versucht, die keys indirekt
879 // ueber den referenzierten MRDR zu besorgen.
880 //   Das Datenelement ReferencedFileID bekommt die Laenge 0.
881 //   indirectViaMRDR == OFTrue
882 //
883 // Existiert kein Verweis auf einen MRDR, so wird davon ausgegangen, das auch
884 // keine SOP-Instanz mit dem aktuellen Directory Record verknuepft werden soll:
885 //   Die Datenelemente ReferencedFileID, MRDRDirectoryRecordOffset,
886 //   ReferencedSOPClassUIDinFile und ReferencedSOPInstanceUIDinFile entfallen.
887 //   !directFromFile && !indirectViaMRDR
888 //
889 
fillElementsAndReadSOP(const char * referencedFileID,const OFFilename & sourceFileName,DcmFileFormat * fileFormat)890 OFCondition DcmDirectoryRecord::fillElementsAndReadSOP(const char *referencedFileID,
891                                                        const OFFilename &sourceFileName,
892                                                        DcmFileFormat *fileFormat)
893 {
894     OFCondition l_error = EC_Normal;
895     OFFilename fileName;
896     DcmFileFormat *refFile = NULL;
897     /* This variable is only set if we created our own DcmFileFormat instance */
898     DcmFileFormat *ownFile = NULL;
899 
900     if (fileFormat != NULL && sourceFileName.isEmpty())
901         return EC_IllegalCall;
902 
903     OFBool directFromFile = OFFalse;
904     OFBool indirectViaMRDR = OFFalse;
905     if (referencedFileID != NULL && *referencedFileID != '\0')
906         directFromFile = OFTrue;
907     else if (DirRecordType != ERT_Mrdr && referencedMRDR != NULL)     // illegal for MRDR
908     {
909         indirectViaMRDR = OFTrue;
910         referencedFileID = referencedMRDR->lookForReferencedFileID();
911     }
912 
913     if (referencedFileID != NULL && *referencedFileID != '\0')
914     {
915         if (sourceFileName.isEmpty())
916         {
917             /* create a new source filename */
918             size_t bufsize = strlen(referencedFileID) + 2;
919             char *newname = new char[bufsize];
920             buildFileName(referencedFileID, newname, bufsize);
921             fileName.set(newname);
922             delete[] newname;
923         } else {
924             /* just copy the source filename */
925             fileName = sourceFileName;
926         }
927 
928         if (DirRecordType != ERT_Mrdr)
929         {
930             if (fileFormat)
931             {
932                 DCMDATA_TRACE("DcmDirectoryRecord::fillElementsAndReadSOP(): Using existing file format for \"" << fileName << "\".");
933                 refFile = fileFormat;
934             } else {
935                 DCMDATA_TRACE("DcmDirectoryRecord::fillElementsAndReadSOP(): Load file \"" << fileName << "\" because our caller didn't do so.");
936                 ownFile = new DcmFileFormat();
937                 l_error = ownFile->loadFile(fileName);
938                 refFile = ownFile;
939                 if (l_error.bad())
940                 {
941                   DCMDATA_ERROR("DcmDirectoryRecord::fillElementsAndReadSOP(): DicomFile \""
942                       << fileName << "\" not found");
943                   directFromFile = OFFalse;
944                   indirectViaMRDR = OFFalse;
945                 }
946             }
947         }
948     } else {
949         directFromFile = OFFalse;
950         indirectViaMRDR = OFFalse;
951     }
952 
953     DcmStack stack;
954     DcmUnsignedLongOffset *uloP;
955     DcmUniqueIdentifier *uiP;
956 
957     DcmTag nextOffTag(DCM_OffsetOfTheNextDirectoryRecord); // (0004,1400)
958     uloP = new DcmUnsignedLongOffset(nextOffTag);
959     uloP->putUint32(Uint32(0));
960     if (insert(uloP, OFFalse).bad())
961         delete uloP;
962 
963     setRecordInUseFlag(0xffff);                           // (0004,1410)
964 
965     DcmTag lowerOffTag(DCM_OffsetOfReferencedLowerLevelDirectoryEntity);
966     uloP = new DcmUnsignedLongOffset(lowerOffTag);        // (0004,1420)
967     uloP->putUint32(Uint32(0));
968     if (insert(uloP, OFFalse).bad())
969         delete uloP;
970 
971     setRecordType(DirRecordType);                         // (0004,1430)
972 
973     DcmTag privRecTag(DCM_PrivateRecordUID);              // (0004,1432)
974     if (DirRecordType == ERT_Private)
975     {
976         uiP = new DcmUniqueIdentifier(privRecTag);
977         if (insert(uiP, OFFalse).bad())
978             delete uiP;
979     } else
980         delete remove(privRecTag);
981 
982     if (directFromFile)                                   // (0004,1500)
983         setReferencedFileID(referencedFileID);
984     else
985     {
986         DcmTag refFileTag(DCM_ReferencedFileID);
987         delete remove(refFileTag);
988     }
989 
990     DcmTag mrdrOffTag(DCM_RETIRED_MRDRDirectoryRecordOffset);     // (0004,1504)
991     if (indirectViaMRDR)
992     {
993         // create pointer attribute to MRDR
994         DcmUnsignedLongOffset *uloP2 = new DcmUnsignedLongOffset(mrdrOffTag);
995         uloP2->putUint32(Uint32(0));
996         uloP2->setNextRecord(referencedMRDR);
997         insert(uloP2, OFTrue);
998     } else
999         delete remove(mrdrOffTag);
1000 
1001     DcmTag refSOPClassTag(DCM_ReferencedSOPClassUIDInFile);
1002     DcmTag refSOPInstTag(DCM_ReferencedSOPInstanceUIDInFile);
1003     DcmTag refFileXferTag(DCM_ReferencedTransferSyntaxUIDInFile);
1004 
1005     if (DirRecordType != ERT_Mrdr && (directFromFile || indirectViaMRDR))
1006     {
1007         if (refFile == NULL)
1008         {
1009             DCMDATA_ERROR("Internal ERROR in DcmDirectoryRecord::fillElementsAndReadSOP()");
1010         }
1011         uiP = new DcmUniqueIdentifier(refSOPClassTag);    // (0004,1510)
1012         if (refFile->search(DCM_SOPClassUID, stack).good())
1013         {
1014             char *uid = NULL;
1015             OFstatic_cast(DcmUniqueIdentifier *, stack.top())->getString(uid);
1016             uiP->putString(uid);
1017         } else {
1018             DCMDATA_ERROR("DcmDirectoryRecord::fillElementsAndReadSOP(): "
1019                 << "can't find SOPClassUID in Dataset: " << fileName);
1020             l_error = EC_CorruptedData;
1021         }
1022         insert(uiP, OFTrue);
1023 
1024         uiP = new DcmUniqueIdentifier(refSOPInstTag);     // (0004,1511)
1025         if (refFile->search(DCM_SOPInstanceUID, stack).good() || refFile->search(DCM_MediaStorageSOPInstanceUID, stack).good())
1026         {
1027             char *uid = NULL;
1028             OFstatic_cast(DcmUniqueIdentifier *, stack.top())->getString(uid);
1029             uiP->putString(uid);
1030         } else {
1031             DCMDATA_ERROR("DcmDirectoryRecord::fillElementsAndReadSOP(): "
1032                 << "can't find SOPInstanceUID neither in Dataset or MetaInfo of file: " << fileName);
1033             l_error = EC_CorruptedData;
1034         }
1035         insert(uiP, OFTrue);
1036 
1037         uiP = new DcmUniqueIdentifier(refFileXferTag);     // (0004,1512)
1038         if (refFile->search(DCM_TransferSyntaxUID, stack).good())
1039         {
1040             char *uid = NULL;
1041             OFstatic_cast(DcmUniqueIdentifier *, stack.top())->getString(uid);
1042             uiP->putString(uid);
1043         } else {
1044             DCMDATA_ERROR("DcmDirectoryRecord::fillElementsAndReadSOP(): "
1045                 << "can't find TransferSyntaxUID in MetaInfo of file: " << fileName);
1046             l_error = EC_CorruptedData;
1047         }
1048         insert(uiP, OFTrue);
1049     }
1050     else  // not only in this case: if (DirRecordType == ERT_Mrdr)
1051     {
1052         // remove SOP UIDs from Directory Record
1053         delete remove(refSOPClassTag);
1054         delete remove(refSOPInstTag);
1055         delete remove(refFileXferTag);
1056     }
1057 
1058     delete ownFile;
1059 
1060     return l_error;
1061 }
1062 
1063 
1064 // ********************************
1065 
1066 
masterInsertSub(DcmDirectoryRecord * dirRec,const unsigned long where)1067 OFCondition DcmDirectoryRecord::masterInsertSub(DcmDirectoryRecord *dirRec,
1068                                                 const unsigned long where)
1069 {
1070     // insert without type check
1071     errorFlag = lowerLevelList->insert(dirRec, where);
1072     return errorFlag;
1073 }
1074 
1075 
1076 // ********************************
1077 
1078 
purgeReferencedFile()1079 OFCondition DcmDirectoryRecord::purgeReferencedFile()
1080 {
1081     OFCondition l_error = EC_Normal;
1082     if (DirRecordType != ERT_root)
1083     {
1084         char *localFileName = NULL;
1085 
1086         // remove filename from directory record
1087         const char *fileName = lookForReferencedFileID();
1088         if (fileName != NULL)
1089         {
1090             size_t buflen = strlen(fileName) + 2;
1091             localFileName = new char[buflen];
1092             buildFileName(fileName, localFileName, buflen);
1093             setReferencedFileID(NULL);
1094         }
1095 
1096         DCMDATA_DEBUG("DcmDirectoryRecord::purgeReferencedFile() trying to purge file "
1097             << localFileName << " from file system");
1098 
1099         if (localFileName != NULL)
1100         {                                 // filename exists
1101             if (unlink(localFileName) != 0)
1102             {
1103                 OFString buffer = OFStandard::getLastSystemErrorCode().message();
1104                 errorFlag = makeOFCondition(OFM_dcmdata, 19, OF_error, buffer.c_str());
1105             }
1106             delete[] localFileName;
1107         } else {                          // no referenced file exists
1108             // do nothing
1109         }
1110     } else
1111         l_error = EC_IllegalCall;
1112 
1113     return l_error;
1114 }
1115 
1116 
1117 // ********************************
1118 // ******** public methods ********
1119 // ********************************
1120 
1121 
ident() const1122 DcmEVR DcmDirectoryRecord::ident() const
1123 {
1124     return EVR_dirRecord;
1125 }
1126 
1127 
1128 // ********************************
1129 
1130 
convertCharacterSet(const OFString & fromCharset,const OFString & toCharset,const size_t flags,const OFBool updateCharset)1131 OFCondition DcmDirectoryRecord::convertCharacterSet(const OFString &fromCharset,
1132                                                     const OFString &toCharset,
1133                                                     const size_t flags,
1134                                                     const OFBool updateCharset)
1135 {
1136     // call the method of the base class; this method is only needed to avoid a compiler warning
1137     return DcmItem::convertCharacterSet(fromCharset, toCharset, flags, updateCharset);
1138 }
1139 
1140 
convertCharacterSet(const OFString & toCharset,const size_t flags,const OFBool ignoreCharset)1141 OFCondition DcmDirectoryRecord::convertCharacterSet(const OFString &toCharset,
1142                                                     const size_t flags,
1143                                                     const OFBool ignoreCharset)
1144 {
1145     // call the method of the base class; this method is only needed to avoid a compiler warning
1146     return DcmItem::convertCharacterSet(toCharset, flags, ignoreCharset);
1147 }
1148 
1149 
convertCharacterSet(DcmSpecificCharacterSet & converter)1150 OFCondition DcmDirectoryRecord::convertCharacterSet(DcmSpecificCharacterSet &converter)
1151 {
1152     DCMDATA_DEBUG("DcmDirectoryRecord::convertCharacterSet() processing directory record with offset "
1153         << getFileOffset());
1154     // handle special case of DICOMDIR: the Specific Character Set (0008,0005)
1155     // can be specified individually for each directory record (i.e. item)
1156     OFCondition status = EC_Normal;
1157     OFString fromCharset;
1158     const OFString toCharset = converter.getDestinationCharacterSet();
1159     // determine value of Specific Character Set (0008,0005) if present in this item
1160     if (findAndGetOFStringArray(DCM_SpecificCharacterSet, fromCharset, OFFalse /*searchIntoSub*/).good() &&
1161         (fromCharset != converter.getSourceCharacterSet()))
1162     {
1163         DcmSpecificCharacterSet newConverter;
1164         // create a new character set converter
1165         DCMDATA_DEBUG("DcmDirectoryRecord::convertCharacterSet() creating a new character set converter for '"
1166             << fromCharset << "'" << (fromCharset.empty() ? " (ASCII)" : "") << " to '"
1167             << toCharset << "'" << (toCharset.empty() ? " (ASCII)" : ""));
1168         // select source and destination character set, use same transliteration mode
1169         status = newConverter.selectCharacterSet(fromCharset, toCharset);
1170         if (status.good())
1171         {
1172             const unsigned cflags = converter.getConversionFlags();
1173             if (cflags > 0)
1174                 status = newConverter.setConversionFlags(cflags);
1175             if (status.good())
1176             {
1177                 // convert all affected element values in the item with the new converter
1178                 status = DcmItem::convertCharacterSet(newConverter);
1179                 // update the Specific Character Set (0008,0005) element
1180                 updateSpecificCharacterSet(status, newConverter);
1181             }
1182         }
1183     } else {
1184         // no Specific Character Set attribute or the same character set,
1185         // so proceed with the given converter
1186         status = DcmItem::convertCharacterSet(converter);
1187     }
1188     return status;
1189 }
1190 
1191 
1192 // ********************************
1193 
1194 
print(STD_NAMESPACE ostream & out,const size_t flags,const int level,const char * pixelFileName,size_t * pixelCounter)1195 void DcmDirectoryRecord::print(STD_NAMESPACE ostream &out,
1196                                const size_t flags,
1197                                const int level,
1198                                const char *pixelFileName,
1199                                size_t *pixelCounter)
1200 {
1201     if (flags & DCMTypes::PF_showTreeStructure)
1202     {
1203         /* print record line */
1204         OFOStringStream oss;
1205         oss << "\"Directory Record\" (offset=$"
1206             << getFileOffset() << ")" << OFStringStream_ends;
1207         OFSTRINGSTREAM_GETSTR(oss, tmpString)
1208         printInfoLine(out, flags, level, tmpString);
1209         OFSTRINGSTREAM_FREESTR(tmpString)
1210         /* print item content */
1211         if (!elementList->empty())
1212         {
1213             DcmObject *dO;
1214             elementList->seek(ELP_first);
1215             do {
1216                 dO = elementList->get();
1217                 dO->print(out, flags, level + 1, pixelFileName, pixelCounter);
1218             } while (elementList->seek(ELP_next));
1219         }
1220         if (lowerLevelList->card() > 0)
1221             lowerLevelList->print(out, flags, level + 1);
1222    } else {
1223         /* print record start line */
1224         OFOStringStream oss;
1225         oss << "\"Directory Record\" " << DRTypeNames[DirRecordType]
1226             << " #=" << card() << OFStringStream_ends;
1227         OFSTRINGSTREAM_GETSTR(oss, tmpString)
1228         printInfoLine(out, flags, level, tmpString);
1229         OFSTRINGSTREAM_FREESTR(tmpString)
1230         /* print record comment line */
1231         if (flags & DCMTypes::PF_useANSIEscapeCodes)
1232             out << DCMDATA_ANSI_ESCAPE_CODE_INFO;
1233         printNestingLevel(out, flags, level);
1234         out << "#  offset=$" << getFileOffset();
1235         if (referencedMRDR != NULL)
1236             out << "  refMRDR=$" << referencedMRDR->getFileOffset();
1237         if (DirRecordType == ERT_Mrdr)
1238             out << "  refCount=" << numberOfReferences;
1239         const char *refFile = getReferencedFileName();
1240         if (refFile != NULL)
1241             out << "  refFileID=\"" << refFile << "\"";
1242         if (flags & DCMTypes::PF_useANSIEscapeCodes)
1243             out << DCMDATA_ANSI_ESCAPE_CODE_RESET;
1244         out << OFendl;
1245         /* print item content */
1246         if (!elementList->empty())
1247         {
1248             DcmObject *dO;
1249             elementList->seek(ELP_first);
1250             do {
1251                 dO = elementList->get();
1252                 dO->print(out, flags, level + 1, pixelFileName, pixelCounter);
1253             } while (elementList->seek(ELP_next));
1254         }
1255         if (lowerLevelList->card() > 0)
1256             lowerLevelList->print(out, flags, level + 1);
1257         /* print record end line */
1258         DcmTag delimItemTag(DCM_ItemDelimitationItemTag);
1259         if (getLengthField() == DCM_UndefinedLength)
1260             printInfoLine(out, flags, level, "\"ItemDelimitationItem\"", &delimItemTag);
1261         else
1262             printInfoLine(out, flags, level, "\"ItemDelimitationItem for re-encoding\"", &delimItemTag);
1263     }
1264 }
1265 
1266 
1267 // ********************************
1268 
1269 
writeXML(STD_NAMESPACE ostream & out,const size_t flags)1270 OFCondition DcmDirectoryRecord::writeXML(STD_NAMESPACE ostream &out,
1271                                          const size_t flags)
1272 {
1273     OFCondition l_error = EC_Normal;
1274     if (flags & DCMTypes::XF_useNativeModel)
1275     {
1276         /* in Native DICOM Model, there is no concept of a DICOMDIR */
1277         l_error = makeOFCondition(OFM_dcmdata, EC_CODE_CannotConvertToXML, OF_error,
1278             "Cannot convert Directory Record to Native DICOM Model");
1279     } else {
1280         /* XML start tag for "item" */
1281         out << "<item";
1282         /* cardinality (number of attributes) = 1..n */
1283         out << " card=\"" << card() << "\"";
1284         /* value length in bytes = 0..max (if not undefined) */
1285         if (getLengthField() != DCM_UndefinedLength)
1286             out << " len=\"" << getLengthField() << "\"";
1287         /* byte offset of the record */
1288         out << " offset=\"" << getFileOffset() << "\"";
1289         out << ">" << OFendl;
1290         /* write item content */
1291         if (!elementList->empty())
1292         {
1293             /* write content of all children */
1294             DcmObject *dO;
1295             elementList->seek(ELP_first);
1296             do {
1297                 dO = elementList->get();
1298                 l_error = dO->writeXML(out, flags);
1299             } while (l_error.good() && elementList->seek(ELP_next));
1300         }
1301         if (l_error.good())
1302         {
1303             if (lowerLevelList->card() > 0)
1304                 lowerLevelList->writeXML(out, flags);
1305             /* XML end tag for "item" */
1306             out << "</item>" << OFendl;
1307         }
1308     }
1309     return l_error;
1310 }
1311 
1312 
1313 // ********************************
1314 
1315 
read(DcmInputStream & inStream,const E_TransferSyntax xfer,const E_GrpLenEncoding glenc,const Uint32 maxReadLength)1316 OFCondition DcmDirectoryRecord::read(DcmInputStream &inStream,
1317                                      const E_TransferSyntax xfer,
1318                                      const E_GrpLenEncoding glenc,
1319                                      const Uint32 maxReadLength)
1320 {
1321     if (getTransferState() == ERW_notInitialized)
1322         errorFlag = EC_IllegalCall;
1323     else
1324     {
1325         if (getTransferState() != ERW_ready)
1326         {
1327             DcmXfer xferSyn(xfer);
1328             errorFlag = DcmItem::read(inStream, xfer, glenc, maxReadLength);
1329             /*
1330             ** Remember the actual file offset for this Directory Record.
1331             ** Compute by subtracting the Item header (tag & length fields)
1332             ** from the start position of data within the Item (fStartPosition).
1333             ** fStartPosition is set in DcmItem::read(...)
1334             ** offsetInFile is used in the print(...) method.
1335             */
1336             offsetInFile = OFstatic_cast(Uint32, fStartPosition) - xferSyn.sizeofTagHeader(getTag().getEVR());
1337         }
1338 
1339         if (getTransferState() == ERW_ready && DirRecordType == ERT_Private)     // minimizes multiple evaluation
1340         {
1341             DirRecordType = lookForRecordType();
1342             if (DirRecordType == ERT_Mrdr)
1343                 numberOfReferences = lookForNumberOfReferences();
1344         }
1345     }
1346     return errorFlag;
1347 }
1348 
1349 
1350 // ********************************
1351 
1352 
verify(const OFBool autocorrect)1353 OFCondition DcmDirectoryRecord::verify(const OFBool autocorrect)
1354 {
1355     OFCondition err1 = EC_Normal;
1356     OFCondition err2 = EC_Normal;
1357     errorFlag = EC_Normal;
1358     if (autocorrect == OFTrue && DirRecordType != ERT_root)
1359         errorFlag = fillElementsAndReadSOP(getReferencedFileName(), "");
1360 
1361     err1 = DcmItem::verify(autocorrect);
1362     err2 = lowerLevelList->verify(autocorrect);
1363     if (errorFlag.good() && (err1.bad() || err2.bad()))
1364         errorFlag = EC_CorruptedData;
1365     return errorFlag;
1366 }
1367 
1368 
1369 // ********************************
1370 
1371 
search(const DcmTagKey & tag,DcmStack & resultStack,E_SearchMode mode,OFBool searchIntoSub)1372 OFCondition DcmDirectoryRecord::search(const DcmTagKey &tag,
1373                                        DcmStack &resultStack,
1374                                        E_SearchMode mode,
1375                                        OFBool searchIntoSub)
1376 {
1377     OFCondition l_error = DcmItem::search(tag, resultStack, mode, searchIntoSub);
1378     if (l_error.bad())
1379     {
1380         if (mode != ESM_afterStackTop || resultStack.top() == this)
1381             resultStack.push(lowerLevelList);
1382         l_error = lowerLevelList->search(tag, resultStack, mode, searchIntoSub);
1383         if (l_error.bad())
1384             resultStack.pop();
1385     }
1386     return l_error;
1387 }
1388 
1389 
1390 // ********************************
1391 
1392 
getRecordType()1393 E_DirRecType DcmDirectoryRecord::getRecordType()
1394 {
1395     return DirRecordType;
1396 }
1397 
1398 
1399 // ********************************
1400 
1401 
getReferencedMRDR()1402 DcmDirectoryRecord* DcmDirectoryRecord::getReferencedMRDR()
1403 {
1404     return referencedMRDR;
1405 }
1406 
1407 
1408 // ********************************
1409 
1410 
assignToSOPFile(const char * referencedFileID,const OFFilename & sourceFileName)1411 OFCondition DcmDirectoryRecord::assignToSOPFile(const char *referencedFileID,
1412                                                 const OFFilename &sourceFileName)
1413 {
1414     errorFlag = EC_Normal;
1415 
1416     if (DirRecordType != ERT_root)
1417     {
1418         DCMDATA_DEBUG("DcmDirectoryRecord::assignToSOPFile() old Referenced File ID was "
1419             << getReferencedFileName());
1420         DCMDATA_DEBUG("new Referenced File ID is " << referencedFileID);
1421 
1422         // update against the old reference counter
1423         if (referencedMRDR != NULL)
1424             referencedMRDR->decreaseRefNum();
1425         referencedMRDR = NULL;
1426 
1427         errorFlag = fillElementsAndReadSOP(referencedFileID, sourceFileName);
1428     } else
1429         errorFlag = EC_IllegalCall;
1430     return errorFlag;
1431 }
1432 
1433 
1434 // ********************************
1435 
1436 
assignToMRDR(DcmDirectoryRecord * mrdr)1437 OFCondition DcmDirectoryRecord::assignToMRDR(DcmDirectoryRecord *mrdr)
1438 {
1439     errorFlag = EC_Normal;
1440     if (DirRecordType != ERT_root
1441         && mrdr != NULL                        // new MRDR available
1442         && mrdr != referencedMRDR              // old MRDR != new MRDR
1443       )
1444     {
1445         DCMDATA_DEBUG("DcmDirectoryRecord::assignToMRDR() old Referenced File ID was "
1446             << getReferencedFileName());
1447         DCMDATA_DEBUG("new Referenced File ID is " << mrdr->lookForReferencedFileID());
1448 
1449         // set internal pointer to mrdr and update against the old value
1450         if (referencedMRDR != NULL)
1451             referencedMRDR->decreaseRefNum();
1452         referencedMRDR = mrdr;
1453 
1454         // increase reference counter in referencedMRDR by one
1455         referencedMRDR->increaseRefNum();
1456 
1457         // set length of Referenced File ID to zero and fill data elements
1458         errorFlag = fillElementsAndReadSOP(NULL, "");
1459     } else
1460         errorFlag = EC_IllegalCall;
1461 
1462     return errorFlag;
1463 }
1464 
1465 
1466 // ********************************
1467 
1468 
cardSub() const1469 unsigned long DcmDirectoryRecord::cardSub() const
1470 {
1471     return lowerLevelList->card();
1472 }
1473 
1474 
1475 // ********************************
1476 
1477 
insertSub(DcmDirectoryRecord * dirRec,unsigned long where,OFBool before)1478 OFCondition DcmDirectoryRecord::insertSub(DcmDirectoryRecord *dirRec,
1479                                           unsigned long where,
1480                                           OFBool before)
1481 {
1482     if (dirRec != NULL)
1483     {
1484         if (checkHierarchy(DirRecordType, dirRec->DirRecordType).good())
1485             errorFlag = lowerLevelList->insert(dirRec, where, before);
1486         else
1487         {
1488             errorFlag = EC_IllegalCall;
1489             DCMDATA_DEBUG("DcmDirectoryRecord::insertSub() dcdirrec: ("
1490                 << DRTypeNames[getRecordType()] << " -> "
1491                 << DRTypeNames[dirRec->getRecordType()] << ") hierarchy not allowed");
1492         }
1493     }
1494     return errorFlag;
1495 }
1496 
1497 
1498 // ********************************
1499 
1500 
insertSubAtCurrentPos(DcmDirectoryRecord * dirRec,OFBool before)1501 OFCondition DcmDirectoryRecord::insertSubAtCurrentPos(DcmDirectoryRecord *dirRec,
1502                                                       OFBool before)
1503 {
1504     if (dirRec != NULL)
1505     {
1506         if (checkHierarchy(DirRecordType, dirRec->DirRecordType).good())
1507             errorFlag = lowerLevelList->insertAtCurrentPos(dirRec, before);
1508         else {
1509             errorFlag = EC_IllegalCall;
1510             DCMDATA_DEBUG("DcmDirectoryRecord::insertSubAtCurrentPos() dcdirrec: ("
1511                 << DRTypeNames[getRecordType()] << " -> " << DRTypeNames[dirRec->getRecordType()]
1512                 << ") hierarchy not allowed");
1513         }
1514     }
1515     return errorFlag;
1516 }
1517 
1518 
1519 // ********************************
1520 
1521 
getSub(const unsigned long num)1522 DcmDirectoryRecord *DcmDirectoryRecord::getSub(const unsigned long num)
1523 {
1524     DcmDirectoryRecord *retRec = OFstatic_cast(DcmDirectoryRecord *, lowerLevelList->getItem(num));
1525     errorFlag = lowerLevelList->error();
1526     return retRec;
1527 }
1528 
1529 
1530 // ********************************
1531 
1532 
nextSub(const DcmDirectoryRecord * dirRec)1533 DcmDirectoryRecord *DcmDirectoryRecord::nextSub(const DcmDirectoryRecord *dirRec)
1534 {
1535     DcmDirectoryRecord *retRec = OFstatic_cast(DcmDirectoryRecord *, lowerLevelList->nextInContainer(dirRec));
1536     errorFlag = lowerLevelList->error();
1537     return retRec;
1538 }
1539 
1540 
1541 // ********************************
1542 
1543 
removeSub(const unsigned long num)1544 DcmDirectoryRecord* DcmDirectoryRecord::removeSub(const unsigned long num)
1545 {
1546     DcmDirectoryRecord *retRec = OFstatic_cast(DcmDirectoryRecord *, lowerLevelList->remove(num));
1547     errorFlag = lowerLevelList->error();
1548     return retRec;
1549 }
1550 
1551 
1552 // ********************************
1553 
1554 
removeSub(DcmDirectoryRecord * dirRec)1555 DcmDirectoryRecord* DcmDirectoryRecord::removeSub(DcmDirectoryRecord *dirRec)
1556 {
1557     DcmDirectoryRecord *retRec = OFstatic_cast(DcmDirectoryRecord *, lowerLevelList->remove(dirRec));
1558     errorFlag = lowerLevelList->error();
1559     return retRec;
1560 }
1561 
1562 
1563 // ********************************
1564 
1565 
deleteSubAndPurgeFile(const unsigned long num)1566 OFCondition DcmDirectoryRecord::deleteSubAndPurgeFile(const unsigned long num)
1567 {
1568     DcmDirectoryRecord *subDirRec = OFstatic_cast(DcmDirectoryRecord *, lowerLevelList->remove(num));
1569     errorFlag = lowerLevelList->error();
1570     if (subDirRec != NULL)
1571     {
1572         DcmDirectoryRecord *localSubRefMRDR = subDirRec->getReferencedMRDR();
1573 
1574         if (localSubRefMRDR != NULL)
1575         {                                   // file is referenced (indirect)
1576             localSubRefMRDR->decreaseRefNum();
1577         } else                              // remove file directly
1578             errorFlag = subDirRec->purgeReferencedFile();
1579 
1580         DCMDATA_DEBUG("DcmDirectoryRecord::deleteSubAndPurgeFile() now purging lower records:");
1581 
1582         while (subDirRec->cardSub() > 0)    // remove all sub sub records
1583             subDirRec->deleteSubAndPurgeFile(OFstatic_cast(unsigned long, 0));
1584         delete subDirRec;                   // remove sub directory record
1585     }
1586     return errorFlag;
1587 }
1588 
1589 
1590 // ********************************
1591 
1592 
deleteSubAndPurgeFile(DcmDirectoryRecord * dirRec)1593 OFCondition DcmDirectoryRecord::deleteSubAndPurgeFile(DcmDirectoryRecord *dirRec)
1594 {
1595     DcmDirectoryRecord *subDirRec = OFstatic_cast(DcmDirectoryRecord *, lowerLevelList->remove(dirRec));
1596     errorFlag = lowerLevelList->error();
1597     if (subDirRec != NULL)
1598     {
1599         DcmDirectoryRecord *localSubRefMRDR = subDirRec->getReferencedMRDR();
1600 
1601         if (localSubRefMRDR != NULL)
1602         {                                   // file is referenced (indirect)
1603             localSubRefMRDR->decreaseRefNum();
1604         } else                              // remove file directly
1605             errorFlag = subDirRec->purgeReferencedFile();
1606 
1607         DCMDATA_DEBUG("DcmDirectoryRecord::deleteSubAndPurgeFile() now purging lower records:");
1608 
1609         while (subDirRec->cardSub() > 0)    // remove all sub sub records
1610             subDirRec->deleteSubAndPurgeFile(OFstatic_cast(unsigned long, 0));
1611         delete subDirRec;                   // remove sub directory record
1612     }
1613     return errorFlag;
1614 }
1615 
1616 
clearSub()1617 OFCondition DcmDirectoryRecord::clearSub()
1618 {
1619     errorFlag = lowerLevelList->clear();
1620     return errorFlag;
1621 }
1622 
1623 
setRecordsOriginFile(const OFFilename & fname)1624 void DcmDirectoryRecord::setRecordsOriginFile(const OFFilename &fname)
1625 {
1626     recordsOriginFile = fname;
1627 }
1628 
getRecordsOriginFile()1629 const OFFilename &DcmDirectoryRecord::getRecordsOriginFile()
1630 {
1631     return recordsOriginFile;
1632 }
1633