Coverage Report

Created: 2026-06-30 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmdata/libsrc/dcbytstr.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 1994-2025, 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 DcmByteString
19
 *
20
 */
21
22
23
#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24
#include "dcmtk/ofstd/ofstream.h"
25
#include "dcmtk/ofstd/ofstring.h"
26
#include "dcmtk/ofstd/ofstd.h"
27
#include "dcmtk/dcmdata/dcjson.h"
28
#include "dcmtk/dcmdata/dcbytstr.h"
29
#include "dcmtk/dcmdata/dcvr.h"
30
#include "dcmtk/dcmdata/dcmatch.h"
31
32
// global flags
33
34
OFGlobal<OFBool> dcmEnableVRCheckerForStringValues(OFTrue);
35
36
37
// global function to get a particular component of a DICOM string
38
OFCondition getStringPart(OFString &result,
39
                          const char *str,
40
                          const unsigned long len,
41
                          const unsigned long pos)
42
0
{
43
0
    OFCondition l_error = EC_Normal;
44
    /* check string parameter */
45
0
    if (str != NULL)
46
0
    {
47
        /* search for beginning of specified string component */
48
0
        unsigned long i = 0;
49
0
        unsigned long curPos = 0;
50
0
        while ((curPos < pos) && (i++ < len))
51
0
        {
52
0
            if (*str++ == '\\')
53
0
                curPos++;
54
0
        }
55
        /* if found ... */
56
0
        if (curPos == pos)
57
0
        {
58
            /* search for end of specified string component */
59
0
            const char *p = str;
60
0
            while ((*p != '\\') && (i++ < len))
61
0
                p++;
62
            /* check whether string component is non-empty */
63
0
            if (p - str > 0)
64
0
                result.assign(str, p - str);
65
0
            else
66
0
                result.clear();
67
0
        } else {
68
            /* specified component index not found in string */
69
0
            l_error = EC_IllegalParameter;
70
0
        }
71
0
    } else
72
0
        l_error = EC_IllegalParameter;
73
0
    return l_error;
74
0
}
75
76
77
// ********************************
78
79
DcmByteString::DcmByteString(const DcmTag &tag)
80
0
  : DcmElement(tag, 0),
81
0
    paddingChar(' '),
82
0
    maxLength(DCM_UndefinedLength),
83
0
    realLength(0),
84
0
    fStringMode(DCM_UnknownString),
85
0
    nonSignificantChars()
86
0
{
87
0
}
88
89
90
DcmByteString::DcmByteString(const DcmTag &tag,
91
                             const Uint32 len)
92
10.7k
  : DcmElement(tag, len),
93
10.7k
    paddingChar(' '),
94
10.7k
    maxLength(DCM_UndefinedLength),
95
10.7k
    realLength(len),
96
10.7k
    fStringMode(DCM_UnknownString),
97
10.7k
    nonSignificantChars()
98
10.7k
{
99
10.7k
}
100
101
102
DcmByteString::DcmByteString(const DcmByteString &old)
103
0
  : DcmElement(old),
104
0
    paddingChar(old.paddingChar),
105
0
    maxLength(old.maxLength),
106
0
    realLength(old.realLength),
107
0
    fStringMode(old.fStringMode),
108
0
    nonSignificantChars(old.nonSignificantChars)
109
0
{
110
0
}
111
112
113
DcmByteString::~DcmByteString()
114
10.7k
{
115
10.7k
}
116
117
118
DcmByteString &DcmByteString::operator=(const DcmByteString &obj)
119
0
{
120
0
    if (this != &obj)
121
0
    {
122
0
        DcmElement::operator=(obj);
123
124
        /* copy member variables */
125
0
        paddingChar = obj.paddingChar;
126
0
        maxLength = obj.maxLength;
127
0
        realLength = obj.realLength;
128
0
        fStringMode = obj.fStringMode;
129
0
        nonSignificantChars = obj.nonSignificantChars;
130
0
    }
131
0
    return *this;
132
0
}
133
134
135
int DcmByteString::compare(const DcmElement& rhs) const
136
0
{
137
0
    int result = DcmElement::compare(rhs);
138
0
    if (result != 0)
139
0
    {
140
0
        return result;
141
0
    }
142
143
    /* cast away constness (dcmdata is not const correct...) */
144
0
    DcmByteString* myThis = NULL;
145
0
    DcmByteString* myRhs = NULL;
146
0
    myThis = OFconst_cast(DcmByteString*, this);
147
0
    myRhs = OFstatic_cast(DcmByteString*, OFconst_cast(DcmElement*, &rhs));
148
149
    /* compare number of values */
150
0
    unsigned long rhsNumValues = myRhs->getNumberOfValues();
151
0
    unsigned long thisNumValues = myThis->getNumberOfValues();
152
0
    if (thisNumValues < rhsNumValues)
153
0
    {
154
0
        return -1;
155
0
    }
156
0
    else if (thisNumValues > rhsNumValues)
157
0
    {
158
0
        return 1;
159
0
    }
160
161
    /* iterate over all components and test equality */
162
0
    for (unsigned long count = 0; count < thisNumValues; count++)
163
0
    {
164
0
        OFString val;
165
0
        if (myThis->getOFString(val, count).good())
166
0
        {
167
0
            OFString rhsVal;
168
0
            if (myRhs->getOFString(rhsVal, count).good())
169
0
            {
170
0
                result = val.compare(rhsVal);
171
0
                if (result != 0)
172
0
                {
173
0
                    return result;
174
0
                }
175
0
            }
176
0
        }
177
0
    }
178
    /* all values equal */
179
0
    return 0;
180
0
}
181
182
183
184
185
OFCondition DcmByteString::copyFrom(const DcmObject& rhs)
186
0
{
187
0
    if (this != &rhs)
188
0
    {
189
0
        if (rhs.ident() != ident()) return EC_IllegalCall;
190
0
        *this = OFstatic_cast(const DcmByteString &, rhs);
191
0
    }
192
0
    return EC_Normal;
193
0
}
194
195
196
// ********************************
197
198
199
DcmEVR DcmByteString::ident() const
200
0
{
201
    /* valid type identifier is set by derived classes */
202
0
    return EVR_UNKNOWN;
203
0
}
204
205
206
unsigned long DcmByteString::getVM()
207
0
{
208
0
    char *str = NULL;
209
0
    Uint32 len = 0;
210
    /* get stored string value */
211
0
    getString(str, len);
212
    /* and determine the VM */
213
0
    return DcmElement::determineVM(str, len);
214
0
}
215
216
217
unsigned long DcmByteString::getNumberOfValues()
218
0
{
219
    /* same as value multiplicity unless overwritten in a derived class */
220
0
    return getVM();
221
0
}
222
223
224
OFCondition DcmByteString::clear()
225
0
{
226
    /* call inherited method */
227
0
    errorFlag = DcmElement::clear();
228
    /* set string representation to unknown */
229
0
    fStringMode = DCM_UnknownString;
230
0
    realLength = 0;
231
0
    return errorFlag;
232
0
}
233
234
235
Uint32 DcmByteString::getRealLength()
236
0
{
237
    /* convert string to internal representation (if required) */
238
0
    if (fStringMode != DCM_MachineString)
239
0
    {
240
        /* strips non-significant trailing spaces (padding) and determines 'realLength' */
241
0
        makeMachineByteString();
242
0
    }
243
    /* string length of the internal representation */
244
0
    return realLength;
245
0
}
246
247
248
Uint32 DcmByteString::getLength(const E_TransferSyntax /*xfer*/,
249
                                const E_EncodingType /*enctype*/)
250
0
{
251
    /* convert string to DICOM representation, i.e. add padding if required */
252
0
    makeDicomByteString();
253
    /* DICOM value length is always an even number */
254
0
    return getLengthField();
255
0
}
256
257
258
// ********************************
259
260
void DcmByteString::print(STD_NAMESPACE ostream& out,
261
                          const size_t flags,
262
                          const int level,
263
                          const char * /*pixelFileName*/,
264
                          size_t * /*pixelCounter*/)
265
0
{
266
0
    if (valueLoaded())
267
0
    {
268
        /* get string data */
269
0
        char *stringVal = NULL;
270
0
        Uint32 stringLen = 0;
271
0
        getString(stringVal, stringLen);
272
0
        if ((stringVal != NULL) && (stringLen > 0))
273
0
        {
274
            /* print line start with tag and VR */
275
0
            printInfoLineStart(out, flags, level);
276
0
            out << '[';
277
278
0
            OFString outString;
279
            /* do not create more output than actually needed */
280
0
            const size_t outStrLen = (flags & DCMTypes::PF_shortenLongTagValues) ? DCM_OptPrintLineLength : 0 /* all characters */;
281
            /* check whether string has to be converted to markup or octal representation */
282
0
            if (flags & DCMTypes::PF_convertToMarkup)
283
0
            {
284
0
                OFString inString(stringVal, stringLen);
285
0
                OFStandard::convertToMarkupString(inString, outString, OFTrue, OFStandard::MM_XML, OFFalse, outStrLen);
286
0
            }
287
0
            else if (flags & DCMTypes::PF_convertToOctalNumbers)
288
0
            {
289
0
                OFString inString(stringVal, stringLen);
290
0
                OFStandard::convertToOctalString(inString, outString, outStrLen);
291
0
            } else {
292
                /* check whether we need the full string or the prefix only */
293
0
                if ((outStrLen == 0) || (outStrLen > stringLen))
294
0
                    outString.assign(stringVal, stringLen);
295
0
                else
296
0
                    outString.assign(stringVal, outStrLen);
297
0
            }
298
299
0
            size_t printedLength = outString.length() + 2 /* for enclosing brackets */;
300
301
            /* check whether full value text should be printed */
302
0
            if ((flags & DCMTypes::PF_shortenLongTagValues) && (printedLength > DCM_OptPrintLineLength))
303
0
            {
304
                /* truncate value text and append "..." */
305
0
                outString.erase(DCM_OptPrintLineLength - 4);
306
0
                out << outString << "...";
307
0
                printedLength = DCM_OptPrintLineLength;
308
0
            } else
309
0
                out << outString << ']';
310
311
            /* print line end with length, VM and tag name */
312
0
            printInfoLineEnd(out, flags, OFstatic_cast(unsigned long, printedLength));
313
0
        } else
314
0
            printInfoLine(out, flags, level, "(no value available)");
315
0
    } else
316
0
        printInfoLine(out, flags, level, "(not loaded)");
317
0
}
318
319
320
// ********************************
321
322
323
OFCondition DcmByteString::write(DcmOutputStream &outStream,
324
                                 const E_TransferSyntax oxfer,
325
                                 const E_EncodingType enctype,
326
                                 DcmWriteCache *wcache)
327
0
{
328
0
    if (getTransferState() == ERW_notInitialized)
329
0
        errorFlag = EC_IllegalCall;
330
0
    else
331
0
    {
332
        /* convert string value to DICOM representation and call inherited method */
333
0
        if (getTransferState() == ERW_init)
334
0
            makeDicomByteString();
335
336
0
        errorFlag = DcmElement::write(outStream, oxfer, enctype, wcache);
337
0
    }
338
0
    return errorFlag;
339
0
}
340
341
342
OFCondition DcmByteString::writeSignatureFormat(DcmOutputStream &outStream,
343
                                                const E_TransferSyntax oxfer,
344
                                                const E_EncodingType enctype,
345
                                                DcmWriteCache *wcache)
346
0
{
347
0
    if (getTransferState() == ERW_notInitialized)
348
0
        errorFlag = EC_IllegalCall;
349
0
    else
350
0
    {
351
        /* convert string value to DICOM representation and call inherited method */
352
0
        if (getTransferState() == ERW_init)
353
0
            makeDicomByteString();
354
0
        errorFlag = DcmElement::writeSignatureFormat(outStream, oxfer, enctype, wcache);
355
0
    }
356
0
    return errorFlag;
357
0
}
358
359
360
// ********************************
361
362
363
OFCondition DcmByteString::getOFString(OFString &stringVal,
364
                                       const unsigned long pos,
365
                                       OFBool /*normalize*/)
366
0
{
367
    /* check given string position index */
368
0
    if (pos >= getVM())
369
0
    {
370
        /* treat an empty string as a special case */
371
0
        if (pos == 0)
372
0
        {
373
0
            errorFlag = EC_Normal;
374
0
            stringVal.clear();
375
0
        } else
376
0
            errorFlag = EC_IllegalParameter;
377
0
    } else {
378
        /* get string data */
379
0
        char *str = NULL;
380
0
        Uint32 len = 0;
381
0
        errorFlag = getString(str, len);
382
        /* check whether string value is present */
383
0
        if ((str != NULL) && (len > 0))
384
0
        {
385
            /* extract specified string component */
386
0
            errorFlag = getStringPart(stringVal, str, len, pos);
387
0
        } else
388
0
            stringVal.clear();
389
0
    }
390
0
    return errorFlag;
391
0
}
392
393
394
OFCondition DcmByteString::getOFStringArray(OFString &stringVal,
395
                                            OFBool normalize)
396
0
{
397
    /* check whether time-consuming normalization is really needed */
398
0
    if (normalize)
399
0
        errorFlag = DcmElement::getOFStringArray(stringVal, normalize);
400
0
    else
401
0
        errorFlag = getStringValue(stringVal);
402
0
    return errorFlag;
403
0
}
404
405
406
OFCondition DcmByteString::getStringValue(OFString &stringVal)
407
0
{
408
0
    char *str = NULL;
409
0
    Uint32 len = 0;
410
0
    errorFlag = getString(str, len);
411
    /* check whether string value is present */
412
0
    if ((str != NULL) && (len > 0))
413
0
        stringVal.assign(str, len);
414
0
    else
415
0
        stringVal.clear();
416
0
    return errorFlag;
417
0
}
418
419
420
OFCondition DcmByteString::getString(char *&stringVal)
421
4.51k
{
422
4.51k
    errorFlag = EC_Normal;
423
    /* get string data */
424
4.51k
    stringVal = OFstatic_cast(char *, getValue());
425
    /* convert to internal string representation (without padding) if required */
426
4.51k
    if ((stringVal != NULL) && (fStringMode != DCM_MachineString))
427
964
        makeMachineByteString();
428
4.51k
    return errorFlag;
429
4.51k
}
430
431
432
OFCondition DcmByteString::getString(char *&stringVal,
433
                                     Uint32 &stringLen)
434
0
{
435
    /* get string data */
436
0
    errorFlag = getString(stringVal);
437
    /* return the real length of the value */
438
0
    stringLen = realLength;
439
0
    return errorFlag;
440
0
}
441
442
443
// ********************************
444
445
446
OFCondition DcmByteString::putString(const char *stringVal)
447
0
{
448
    /* determine length of the string value */
449
0
    const size_t stringLen = (stringVal != NULL) ? strlen(stringVal) : 0;
450
    /* call the real function */
451
0
    return putString(stringVal, OFstatic_cast(Uint32, stringLen));
452
0
}
453
454
455
OFCondition DcmByteString::putString(const char *stringVal,
456
                                     const Uint32 stringLen)
457
0
{
458
0
    errorFlag = EC_Normal;
459
    /* check for an empty string parameter */
460
0
    if ((stringVal != NULL) && (stringLen > 0))
461
0
        putValue(stringVal, stringLen);
462
0
    else
463
0
        putValue(NULL, 0);
464
    /* make sure that extra padding is removed from the string */
465
0
    fStringMode = DCM_UnknownString;
466
0
    makeMachineByteString(stringLen);
467
0
    return errorFlag;
468
0
}
469
470
471
OFCondition DcmByteString::putOFStringAtPos(const OFString& stringVal,
472
                                            const unsigned long pos)
473
0
{
474
0
    OFCondition result;
475
    // Get old value
476
0
    OFString str;
477
0
    result = getOFStringArray( str );
478
0
    if (result.good())
479
0
    {
480
0
        size_t currentVM = getNumberOfValues();
481
        // Trivial case: No values are set and new value should go to first position
482
0
        if ( (currentVM == 0) && (pos == 0))
483
0
            return putOFStringArray(stringVal);
484
485
        // 1st case: Insert at the end
486
        // If we insert at a position that does not yet exist, append missing number of components by
487
        // adding the corresponding number of backspace chars, append new float value and return.
488
0
        size_t futureVM = pos + 1;
489
0
        if (futureVM > currentVM)
490
0
        {
491
0
            str = str.append(currentVM == 0 ? futureVM - currentVM - 1 : futureVM - currentVM, '\\');
492
0
            str = str.append(stringVal);
493
0
            return putOFStringArray(str);
494
0
        }
495
496
        // 2nd case: New value should be at position 0
497
0
        size_t rightPos = 0;
498
0
        if (pos == 0)
499
0
        {
500
            // First value is empty: Insert new value
501
0
            if (str[0] == '\\')
502
0
            {
503
0
                str = str.insert(0, stringVal);
504
0
            }
505
            // First value is set: Replace old value with new value
506
0
            else
507
0
            {
508
0
                rightPos = str.find_first_of('\\', 0);
509
0
                str = str.replace(0, rightPos, stringVal);
510
0
            }
511
0
            return putOFStringArray(str);
512
0
        }
513
514
        // 3rd case: New value should be inserted somewhere in the middle
515
0
        size_t leftPos = 0;
516
0
        size_t vmPos = 0;
517
        // First, find the correct position, and then insert / replace new value
518
0
        do
519
0
        {
520
            // Step from value to value by looking for delimiters.
521
            // Special handling first search (start looking at position 0 instead of 1)
522
0
            if (vmPos == 0) leftPos = str.find('\\', 0);
523
0
            else leftPos = str.find('\\', leftPos + 1 );
524
            // leftPos = str.find('\\', leftPos == 0 ? 0 : leftPos +1);
525
0
            if (leftPos != OFString_npos)
526
0
            {
527
0
                vmPos++;
528
0
            }
529
0
        }
530
0
        while ( (leftPos != OFString_npos) && (vmPos != pos) );
531
0
        rightPos = str.find_first_of('\\', leftPos+1);
532
0
        if (rightPos == OFString_npos) rightPos = str.length();
533
534
        // If we do not have an old value of size 1 or we have an empty value
535
0
        if (rightPos - leftPos == 1)
536
0
        {
537
            // Empty value
538
0
            if (str.at(leftPos) == '\\')
539
0
                str = str.insert(rightPos, stringVal);
540
            // Old value (length 1)
541
0
            else
542
0
                str = str.replace(leftPos, 1, stringVal);
543
0
        }
544
        // Otherwise replace existing old value (length > 1)
545
0
        else
546
0
        {
547
0
            str = str.replace(leftPos+1, rightPos - leftPos - 1, stringVal);
548
0
        }
549
        // Finally re-insert all values include new value
550
0
        result = putOFStringArray( str );
551
0
    }
552
0
    return result;
553
0
}
554
555
556
// ********************************
557
558
559
OFCondition DcmByteString::makeDicomByteString()
560
0
{
561
    /* get string data */
562
0
    char *value = NULL;
563
0
    errorFlag = getString(value);
564
0
    if (value != NULL)
565
0
    {
566
        /* check for odd length */
567
0
        if (realLength & 1)
568
0
        {
569
            /* if so add a padding character */
570
0
            setLengthField(realLength + 1);
571
0
            value[realLength] = paddingChar;
572
0
        } else if (realLength < getLengthField())
573
0
            setLengthField(realLength);
574
        /* terminate string (removes additional trailing padding characters) */
575
0
        value[getLengthField()] = '\0';
576
0
    }
577
    /* current string representation is now the DICOM one */
578
0
    fStringMode = DCM_DicomString;
579
0
    return errorFlag;
580
0
}
581
582
583
OFCondition DcmByteString::makeMachineByteString(const Uint32 length)
584
964
{
585
964
    errorFlag = EC_Normal;
586
    /* get string data */
587
964
    char *value = OFstatic_cast(char *, getValue());
588
964
    if (value != NULL)
589
964
    {
590
        /* check whether string representation is not the internal one */
591
964
        if (fStringMode != DCM_MachineString)
592
964
        {
593
            /* determine initial string length */
594
964
            realLength = (length == 0) ? getLengthField() : length;
595
            /* remove all trailing spaces if automatic input data correction is enabled */
596
964
            if (dcmEnableAutomaticInputDataCorrection.get())
597
964
            {
598
                /*
599
                ** This code removes extra padding characters at the end of a ByteString.
600
                ** Trailing padding can cause problems when comparing strings.  This kind
601
                ** of padding is non-significant for all string-based value representations.
602
                */
603
964
                if (realLength > 0)
604
964
                {
605
964
                    size_t i = OFstatic_cast(size_t, realLength);
606
2.11k
                    while ((i > 0) && ((value[i - 1] == paddingChar) || (value[i - 1] == '\0')))
607
1.14k
                        value[--i] = '\0';
608
964
                    realLength = OFstatic_cast(Uint32, i);
609
964
                }
610
964
            }
611
964
        }
612
964
    } else
613
0
        realLength = 0;
614
    /* current string representation is now the internal one */
615
964
    fStringMode = DCM_MachineString;
616
964
    return errorFlag;
617
964
}
618
619
620
// ********************************
621
622
623
Uint8 *DcmByteString::newValueField()
624
9.17k
{
625
9.17k
    Uint8 *value = NULL;
626
9.17k
    Uint32 lengthField = getLengthField();
627
    /* check for odd length (in case of a protocol error) */
628
9.17k
    if (lengthField & 1)
629
2.34k
    {
630
2.34k
        if (lengthField == DCM_UndefinedLength)
631
3
        {
632
            /* Print an error message when private attribute states to have an odd length
633
             * equal to the maximum length, because we are not able then to make this value even (+1)
634
             * which would an overflow on some systems as well as being illegal in DICOM
635
             */
636
3
            DCMDATA_WARN("DcmByteString: Element " << getTagName() << " " << getTag()
637
3
                << " has odd maximum length (" << DCM_UndefinedLength << ") and therefore is not loaded");
638
3
            errorFlag = EC_CorruptedData;
639
3
            return NULL;
640
3
        }
641
        /* allocate space for extra padding character (required for the DICOM representation of the string) */
642
643
        // we want to use a non-throwing new here if available.
644
        // If the allocation fails, we report an EC_MemoryExhausted error
645
        // back to the caller.
646
2.34k
        value = new (std::nothrow) Uint8[lengthField + 2];
647
648
        /* terminate string after real length */
649
2.34k
        if (value != NULL)
650
2.29k
        {
651
2.29k
            value[lengthField] = 0;
652
2.29k
            value[lengthField+1] = 0;
653
2.29k
        }
654
655
        /* enforce old (pre DCMTK 3.5.2) behaviour? */
656
2.34k
        if (!dcmAcceptOddAttributeLength.get())
657
0
        {
658
            /* make length even */
659
0
            lengthField++;
660
0
            setLengthField(lengthField);
661
0
        }
662
6.83k
    } else {
663
        /* length is even, but we need an extra byte for the terminating 0 byte */
664
665
        // we want to use a non-throwing new here if available.
666
        // If the allocation fails, we report an EC_MemoryExhausted error
667
        // back to the caller.
668
6.83k
        value = new (std::nothrow) Uint8[lengthField + 1];
669
6.83k
    }
670
    /* make sure that the string is properly terminated by a 0 byte */
671
9.17k
    if (value != NULL)
672
9.11k
        value[lengthField] = 0;
673
58
    else
674
58
        errorFlag = EC_MemoryExhausted;
675
9.17k
    return value;
676
9.17k
}
677
678
679
// ********************************
680
681
682
void DcmByteString::postLoadValue()
683
9.05k
{
684
    /* initially, after loading an attribute the string mode is unknown */
685
9.05k
    fStringMode = DCM_UnknownString;
686
    /* correct value length if automatic input data correction is enabled */
687
9.05k
    if (dcmEnableAutomaticInputDataCorrection.get())
688
9.05k
    {
689
        /* check for odd length */
690
9.05k
        if (getLengthField() & 1)
691
2.26k
        {
692
            // newValueField always allocates an even number of bytes and sets
693
            // the pad byte to zero, so we can safely increase Length here.
694
2.26k
            setLengthField(getLengthField() + 1);
695
2.26k
        }
696
9.05k
    }
697
9.05k
}
698
699
700
// ********************************
701
702
703
OFCondition DcmByteString::verify(const OFBool autocorrect)
704
0
{
705
0
    char *str = NULL;
706
0
    Uint32 len = 0;
707
    /* get string data */
708
0
    errorFlag = getString(str, len);
709
    /* check for non-empty string */
710
0
    if ((str != NULL) && (len > 0))
711
0
    {
712
        /* check whether there is anything to verify at all */
713
0
        if (maxLength != DCM_UndefinedLength)
714
0
        {
715
0
            const unsigned long vm = getVM();
716
            /* TODO: is it really a good idea to create a copy of the string? */
717
0
            OFString value(str, len);
718
0
            size_t posStart = 0;
719
0
            unsigned long vmNum = 0;
720
            /* check all string components */
721
0
            while (posStart != OFString_npos)
722
0
            {
723
0
                ++vmNum;
724
                /* search for next component separator */
725
0
                size_t posEnd = (vm > 1) ? value.find('\\', posStart) : OFString_npos;
726
0
                const size_t fieldLen = (posEnd == OFString_npos) ? value.length() - posStart : posEnd - posStart;
727
                /* check size limit for each string component */
728
0
                if (fieldLen > maxLength)
729
0
                {
730
0
                    DCMDATA_DEBUG("DcmByteString::verify() Maximum length violated in element "
731
0
                        << getTagName() << " " << getTag() << " value " << vmNum << ": " << fieldLen
732
0
                        << " bytes found but only " << maxLength << " bytes allowed");
733
0
                    errorFlag = EC_MaximumLengthViolated;
734
0
                    if (autocorrect)
735
0
                    {
736
0
                        const size_t excess = fieldLen - maxLength;
737
0
                        DCMDATA_DEBUG("DcmByteString::verify() Removing " << excess
738
0
                            << " bytes from the end of value " << vmNum);
739
                        /* erase excessive part of the string component */
740
0
                        value.erase(posStart + maxLength, excess);
741
                        /* correct the position of the end marker by the number of bytes
742
                           we just cut off, if the end of the string is not already reached */
743
0
                        if (posEnd != OFString_npos)
744
0
                            posEnd -= excess;
745
0
                    }
746
0
                }
747
0
                posStart = (posEnd == OFString_npos) ? posEnd : posEnd + 1;
748
0
            }
749
            /* replace current string value if auto correction is enabled */
750
0
            if (autocorrect && errorFlag.bad())
751
0
            {
752
0
                putOFStringArray(value);
753
                /* the above method also sets 'errorFlag', so we need to assign the error code again */
754
0
                errorFlag = EC_MaximumLengthViolated;
755
0
            }
756
0
        }
757
0
    }
758
    /* report a debug message if an error occurred */
759
0
    if (errorFlag.bad())
760
0
    {
761
0
        DCMDATA_WARN("DcmByteString: One or more illegal values in element "
762
0
            << getTagName() << " " << getTag() << " with VM=" << getVM());
763
0
    }
764
0
    return errorFlag;
765
0
}
766
767
768
OFBool DcmByteString::containsExtendedCharacters(const OFBool checkAllStrings)
769
0
{
770
0
    OFBool result = OFFalse;
771
    /* only check if parameter is true since derived VRs are not affected
772
       by the attribute SpecificCharacterSet (0008,0005) */
773
0
    if (checkAllStrings)
774
0
    {
775
0
        char *str = NULL;
776
0
        Uint32 len = 0;
777
        /* determine length in order to support possibly embedded NULL bytes */
778
0
        if (getString(str, len).good())
779
0
            result = containsExtendedCharacters(str, len);
780
0
    }
781
0
    return result;
782
0
}
783
784
785
OFBool DcmByteString::isAffectedBySpecificCharacterSet() const
786
0
{
787
0
    return OFFalse;
788
0
}
789
790
791
// ********************************
792
793
794
OFBool DcmByteString::isEmpty(const OFBool normalize)
795
0
{
796
0
    OFBool result = OFFalse;
797
0
    if (normalize && !nonSignificantChars.empty())
798
0
    {
799
0
        OFString value;
800
0
        DcmByteString::getStringValue(value);
801
        /* check whether string value consists of non-significant characters only */
802
0
        result = (value.find_first_not_of(nonSignificantChars) == OFString_npos);
803
0
    } else
804
0
        result = DcmObject::isEmpty(normalize);
805
0
    return result;
806
0
}
807
808
809
// ********************************
810
811
812
// global function for normalizing a DICOM string
813
void normalizeString(OFString &string,
814
                     const OFBool multiPart,
815
                     const OFBool leading,
816
                     const OFBool trailing,
817
                     const char paddingChar)
818
0
{
819
    /* check for non-empty string */
820
0
    if (!string.empty())
821
0
    {
822
0
        size_t partindex = 0;
823
0
        size_t offset = 0;
824
0
        size_t len = string.length();
825
0
        while (partindex < len)
826
0
        {
827
            // remove leading spaces in every part of the string
828
0
            if (leading)
829
0
            {
830
0
                offset = 0;
831
0
                while ((partindex + offset < len) && (string[partindex + offset] == paddingChar))
832
0
                    offset++;
833
0
                if (offset > 0)
834
0
                    string.erase(partindex, offset);
835
0
            }
836
0
            len = string.length();
837
            // compute begin to the next separator index!
838
0
            if (multiPart)
839
0
            {
840
0
                partindex = string.find('\\', partindex);
841
0
                if (partindex == OFString_npos)
842
0
                    partindex = len;
843
0
            } else
844
0
                partindex = len;
845
            // remove trailing spaces in every part of the string
846
0
            if (trailing && partindex)
847
0
            {
848
0
                offset = partindex - 1;
849
0
                while ((offset > 0) && (string[offset] == paddingChar))
850
0
                    offset--;
851
0
                if (offset != partindex - 1)
852
0
                {
853
0
                    if (string[offset] == ' ')
854
0
                    {
855
0
                        string.erase(offset, partindex - offset);
856
0
                        partindex = offset;
857
0
                    } else {
858
0
                        string.erase(offset + 1, partindex - offset - 1);
859
0
                        partindex = offset + 1;
860
0
                    }
861
0
                }
862
0
            }
863
0
            len = string.length();
864
0
            if (partindex != len)
865
0
                ++partindex;
866
0
        }
867
0
    }
868
0
}
869
870
871
// ********************************
872
873
874
OFBool DcmByteString::containsExtendedCharacters(const char *stringVal,
875
                                                 const size_t stringLen)
876
0
{
877
0
    if (stringVal != NULL)
878
0
    {
879
0
        for (size_t i = stringLen; i != 0; --i)
880
0
        {
881
            /* check for 8 bit characters */
882
0
            if (OFstatic_cast(unsigned char, *stringVal++) > 127)
883
0
                return OFTrue;
884
0
        }
885
0
    }
886
0
    return OFFalse;
887
0
}
888
889
890
// ********************************
891
892
893
OFCondition DcmByteString::checkStringValue(const OFString &value,
894
                                            const OFString &vm,
895
                                            const OFString &vr,
896
                                            const int vrID,
897
                                            const size_t maxLen,
898
                                            const OFString &charset)
899
0
{
900
0
    OFCondition result = EC_Normal;
901
0
    const size_t valLen = value.length();
902
0
    if (valLen > 0)
903
0
    {
904
        /* do we need to search for value components at all? */
905
0
        if (vm.empty())
906
0
        {
907
            /* check value length (if a maximum is specified) */
908
0
            if ((maxLen > 0) && (value.length() > maxLen))
909
0
                result = EC_MaximumLengthViolated;
910
0
            else if (dcmEnableVRCheckerForStringValues.get())
911
0
            {
912
                /* check for non-ASCII characters (if default character set used) */
913
0
                if (charset.empty() || (charset == "ISO_IR 6"))
914
0
                {
915
0
                    if (containsExtendedCharacters(value.c_str(), value.length()))
916
0
                        result = EC_InvalidCharacter;
917
0
                }
918
0
                if (result.good())
919
0
                {
920
                    /* currently, the VR checker only supports ASCII and Latin-1 */
921
0
                    if (charset.empty() || (charset == "ISO_IR 6") || (charset == "ISO_IR 100"))
922
0
                    {
923
                        /* check value representation (VR) */
924
0
                        if (DcmElement::scanValue(value, vr) != vrID)
925
0
                            result = EC_ValueRepresentationViolated;
926
0
                    }
927
0
                }
928
0
            }
929
0
        } else {
930
0
            size_t posStart = 0;
931
0
            unsigned long vmNum = 0;
932
            /* iterate over all value components */
933
0
            while (posStart != OFString_npos)
934
0
            {
935
0
                ++vmNum;
936
                /* search for next component separator */
937
0
                const size_t posEnd = value.find('\\', posStart);
938
0
                const size_t length = (posEnd == OFString_npos) ? valLen - posStart : posEnd - posStart;
939
                /* check length of current value component */
940
0
                if ((maxLen > 0) && (length > maxLen))
941
0
                {
942
0
                    result = EC_MaximumLengthViolated;
943
0
                    break;
944
0
                }
945
0
                else if (dcmEnableVRCheckerForStringValues.get())
946
0
                {
947
                    /* check for non-ASCII characters (if default character set used) */
948
0
                    if (charset.empty() || (charset == "ISO_IR 6"))
949
0
                    {
950
0
                        if (containsExtendedCharacters(value.c_str() + posStart, length))
951
0
                        {
952
0
                            result = EC_InvalidCharacter;
953
0
                            break;
954
0
                        }
955
0
                    }
956
                    /* currently, the VR checker only supports ASCII and Latin-1 */
957
0
                    if (charset.empty() || (charset == "ISO_IR 6") || (charset == "ISO_IR 100"))
958
0
                    {
959
                        /* check value representation (VR) */
960
0
                        if (DcmElement::scanValue(value, vr, posStart, length) != vrID)
961
0
                        {
962
0
                            result = EC_ValueRepresentationViolated;
963
0
                            break;
964
0
                        }
965
0
                    }
966
0
                }
967
0
                posStart = (posEnd == OFString_npos) ? posEnd : posEnd + 1;
968
0
            }
969
0
            if (result.good())
970
0
            {
971
                /* check value multiplicity (VM) */
972
0
                result = DcmElement::checkVM(vmNum, vm);
973
0
            }
974
0
        }
975
0
    }
976
0
    return result;
977
0
}
978
979
980
// ********************************
981
982
983
OFCondition DcmByteString::writeJson(STD_NAMESPACE ostream &out,
984
                                     DcmJsonFormat &format)
985
0
{
986
    /* always write JSON Opener */
987
0
    DcmElement::writeJsonOpener(out, format);
988
    /* write element value (if non-empty) */
989
0
    if (!isEmpty())
990
0
    {
991
0
        OFString value;
992
0
        OFCondition status = getOFString(value, 0L);
993
0
        if (status.bad())
994
0
            return status;
995
0
        format.printValuePrefix(out);
996
0
        DcmJsonFormat::printValueString(out, value);
997
0
        const unsigned long vm = getVM();
998
0
        for (unsigned long valNo = 1; valNo < vm; ++valNo)
999
0
        {
1000
0
            status = getOFString(value, valNo);
1001
0
            if (status.bad())
1002
0
                return status;
1003
0
            format.printNextArrayElementPrefix(out);
1004
0
            DcmJsonFormat::printValueString(out, value);
1005
0
        }
1006
0
        format.printValueSuffix(out);
1007
0
    }
1008
    /* write JSON Closer  */
1009
0
    DcmElement::writeJsonCloser(out, format);
1010
    /* always report success */
1011
0
    return EC_Normal;
1012
0
}
1013
1014
1015
OFBool DcmByteString::matches(const DcmElement& candidate,
1016
                              const OFBool enableWildCardMatching) const
1017
0
{
1018
0
  if (ident() == candidate.ident())
1019
0
  {
1020
    // some const casts to call the getter functions, I do not modify the values, I promise!
1021
0
    DcmByteString& key = OFconst_cast(DcmByteString&,*this);
1022
0
    DcmElement& can = OFconst_cast(DcmElement&,candidate);
1023
0
    OFString a, b;
1024
0
    for (unsigned long ui = 0; ui < key.getVM(); ++ui)
1025
0
      for (unsigned long uj = 0; uj < can.getVM(); ++uj)
1026
0
        if( key.getOFString( a, ui, OFTrue ).good() && can.getOFString( b, uj, OFTrue ).good() && matches( a, b, enableWildCardMatching ) )
1027
0
          return OFTrue;
1028
0
    return key.getVM() == 0;
1029
0
  }
1030
0
  return OFFalse;
1031
0
}
1032
1033
1034
OFBool DcmByteString::matches(const OFString& key,
1035
                              const OFString& candidate,
1036
                              const OFBool enableWildCardMatching) const
1037
0
{
1038
0
  OFstatic_cast(void,enableWildCardMatching);
1039
  // Universal Matching || Single Value Matching
1040
0
  return key.empty() || key == candidate;
1041
0
}