Coverage Report

Created: 2026-06-30 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/ofstd/libsrc/ofstd.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 2001-2026, 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
 *  As an exception of the above notice, the code for OFStandard::strlcpy
15
 *  and OFStandard::strlcat in this file have been derived from the BSD
16
 *  implementation which carries the following copyright notice:
17
 *
18
 *  Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
19
 *  All rights reserved.  See COPYRIGHT file for details.
20
 *
21
 *  Redistribution and use in source and binary forms, with or without
22
 *  modification, are permitted provided that the following conditions
23
 *  are met:
24
 *  1. Redistributions of source code must retain the above copyright
25
 *     notice, this list of conditions and the following disclaimer.
26
 *  2. Redistributions in binary form must reproduce the above copyright
27
 *     notice, this list of conditions and the following disclaimer in the
28
 *     documentation and/or other materials provided with the distribution.
29
 *  3. The name of the author may not be used to endorse or promote products
30
 *     derived from this software without specific prior written permission.
31
 *
32
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
33
 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
34
 *  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
35
 *  THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
37
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
38
 *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
39
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
40
 *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41
 *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42
 *
43
 *
44
 *  The code for OFStandard::atof that is used when DCMTK is compiled
45
 *  with the macro ENABLE_OLD_OFSTD_ATOF_IMPLEMENTATION has been derived
46
 *  from an implementation that carries the following copyright notice:
47
 *
48
 *  Copyright 1988 Regents of the University of California
49
 *  Permission to use, copy, modify, and distribute this software and
50
 *  its documentation for any purpose and without fee is hereby granted,
51
 *  provided that the above copyright notice appear in all copies.  The
52
 *  University of California makes no representations about the
53
 *  suitability of this software for any purpose.  It is provided "as
54
 *  is" without express or implied warranty.
55
 *
56
 *
57
 *  The code for OFStandard::ftoa that is used when DCMTK is compiled
58
 *  with the macro ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION has been derived
59
 *  from an implementation that carries the following copyright notice:
60
 *
61
 *  Copyright (c) 1988 Regents of the University of California.
62
 *  All rights reserved.  See COPYRIGHT file for details.
63
 *
64
 *  Redistribution and use in source and binary forms are permitted
65
 *  provided that the above copyright notice and this paragraph are
66
 *  duplicated in all such forms and that any documentation,
67
 *  advertising materials, and other materials related to such
68
 *  distribution and use acknowledge that the software was developed
69
 *  by the University of California, Berkeley.  The name of the
70
 *  University may not be used to endorse or promote products derived
71
 *  from this software without specific prior written permission.
72
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
73
 *  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
74
 *  WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
75
 *
76
 *
77
 *  The "Base64" encoder/decoder has been derived from an implementation
78
 *  with the following copyright notice:
79
 *
80
 *  Copyright (c) 1999, Bob Withers - bwit@pobox.com
81
 *
82
 *  This code may be freely used for any purpose, either personal or
83
 *  commercial, provided the authors copyright notice remains intact.
84
 *
85
 *
86
 *  Module: ofstd
87
 *
88
 *  Author: Joerg Riesmeier, Marco Eichelberg
89
 *
90
 *  Purpose: Class for various helper functions
91
 *
92
 */
93
94
95
#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
96
97
#ifdef __SUNPRO_CC
98
BEGIN_EXTERN_C
99
// SunPro declares vsnprintf() only in <stdio.h>, not in <cstdio>.
100
#include <stdio.h>
101
END_EXTERN_C
102
#else
103
#include <cstdio>
104
#endif
105
106
#include "dcmtk/ofstd/ofstd.h"
107
#include "dcmtk/ofstd/ofcond.h"
108
#include "dcmtk/ofstd/offile.h"
109
#include "dcmtk/ofstd/ofstream.h"
110
#include "dcmtk/ofstd/oftuple.h"
111
#include "dcmtk/ofstd/ofmath.h"
112
#include "dcmtk/ofstd/ofsockad.h"
113
#include "dcmtk/ofstd/ofvector.h"
114
#include "dcmtk/ofstd/ofdiag.h"
115
#include "dcmtk/ofstd/oftimer.h"
116
117
118
#include <cmath>
119
#include <clocale>
120
#include <cstring>       /* for memset() */
121
#include <sstream>
122
123
BEGIN_EXTERN_C
124
#include <sys/stat.h>    /* for stat() */
125
#ifdef HAVE_IO_H
126
#include <io.h>          /* for access() on Win32 */
127
#endif
128
#include <sys/types.h>   /* for opendir() and closedir() */
129
#ifdef HAVE_DIRENT_H
130
#include <dirent.h>      /* for opendir() and closedir() */
131
#else
132
#define dirent direct
133
#ifdef HAVE_SYS_DIR_H
134
#include <sys/dir.h>
135
#endif
136
#endif
137
#ifdef HAVE_FNMATCH_H
138
#include <fnmatch.h>     /* for fnmatch() */
139
#endif
140
#ifdef HAVE_IEEEFP_H
141
#include <ieeefp.h>      /* for finite() on Solaris 2.5.1 */
142
#endif
143
#ifdef HAVE_SYS_UTSNAME_H
144
#include <sys/utsname.h>
145
#endif
146
#ifdef HAVE_SYS_SOCKET_H
147
#include <sys/socket.h>
148
#endif
149
#ifdef HAVE_NETINET_IN_H
150
#include <netinet/in.h>
151
#endif
152
#ifdef HAVE_NETDB_H
153
#include <netdb.h>
154
#endif
155
END_EXTERN_C
156
157
#ifdef HAVE_WINDOWS_H
158
#define WIN32_LEAN_AND_MEAN
159
#include <winsock2.h>
160
#include <windows.h>     /* for GetFileAttributes() */
161
#include <direct.h>      /* for _mkdir() */
162
#include <lm.h>          /* for NetWkstaUserGetInfo */
163
#include <ws2tcpip.h>    /* for struct sockaddr_in6 */
164
#ifndef R_OK /* Windows defines access() but not the constants */
165
#define W_OK 02 /* Write permission */
166
#define R_OK 04 /* Read permission */
167
#define F_OK 00 /* Existence only */
168
#endif /* !R_OK */
169
170
#elif defined(HAVE_WINSOCK_H)
171
#include <winsock.h>  /* include winsock.h directly i.e. on MacOS */
172
#endif /* HAVE_WINDOWS_H */
173
174
#ifdef _WIN32
175
#include <process.h>     /* needed for declaration of getpid() */
176
#endif
177
178
#include "dcmtk/ofstd/ofgrp.h"
179
#include "dcmtk/ofstd/ofpwd.h"
180
#include "dcmtk/ofstd/ofoption.h"
181
182
// check mutually exclusive feature macros for the implementations of OFStandard::atof() and OFStandard::ftoa()
183
184
#if defined (ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION) && defined (ENABLE_CSTDIO_BASED_ATOF_IMPLEMENTATION)
185
#error The macros ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION and ENABLE_CSTDIO_BASED_ATOF_IMPLEMENTATION must not both be defined
186
#endif
187
188
#if defined (ENABLE_OLD_OFSTD_ATOF_IMPLEMENTATION) && defined (ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION)
189
#error The macros ENABLE_OLD_OFSTD_ATOF_IMPLEMENTATION and ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION must not both be defined
190
#endif
191
192
#if defined (ENABLE_OLD_OFSTD_ATOF_IMPLEMENTATION) && defined (ENABLE_CSTDIO_BASED_ATOF_IMPLEMENTATION)
193
#error The macros ENABLE_OLD_OFSTD_ATOF_IMPLEMENTATION and ENABLE_CSTDIO_BASED_ATOF_IMPLEMENTATION must not both be defined
194
#endif
195
196
#if defined (ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION) && defined (ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION)
197
#error The macros ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION and ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION must not both be defined
198
#endif
199
200
#if defined (ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION) && defined (ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION)
201
#error The macros ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION and ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION must not both be defined
202
#endif
203
204
#if defined (ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION) && defined (ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION)
205
#error The macros ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION and ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION must not both be defined
206
#endif
207
208
// define defaults for OFStandard::atof() and OFStandard::ftoa()
209
#if !defined(ENABLE_OLD_OFSTD_ATOF_IMPLEMENTATION) && !defined(ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION) && !defined(ENABLE_CSTDIO_BASED_ATOF_IMPLEMENTATION)
210
#ifdef _WIN32
211
// on Windows, the iostream-based implementation of atof is extremely slow,
212
// and we do have a locale independent version of sscanf. Use this version.
213
#define ENABLE_CSTDIO_BASED_ATOF_IMPLEMENTATION
214
#else
215
// on other platforms, we assume that the iobased-implementation, being the
216
// cleanest one, is appropriate. This is known to be the case for gcc and clang with glibc.
217
#define ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION
218
#endif
219
#endif
220
221
#if !defined(ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION) && !defined(ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION) && !defined(ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION)
222
#ifdef _WIN32
223
// on Windows, the iostream-based implementation of atof is extremely slow,
224
// and we do have a locale independent version of sprintf, called _snprintf_s_l.
225
// Use this version.
226
#define ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION
227
#else
228
// on other platforms, we assume that the iobased-implementation, being the
229
// cleanest one, is appropriate. This is known to be the case for gcc and clang with glibc.
230
#define ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION
231
#endif
232
#endif
233
234
// maximum number of repetitions for EAI_AGAIN
235
0
#define DCMTK_MAX_EAI_AGAIN_REPETITIONS 5
236
237
// --- ftoa() processing flags ---
238
239
const unsigned int OFStandard::ftoa_format_e  = 0x01;
240
const unsigned int OFStandard::ftoa_format_f  = 0x02;
241
const unsigned int OFStandard::ftoa_uppercase = 0x04;
242
const unsigned int OFStandard::ftoa_alternate = 0x08;
243
const unsigned int OFStandard::ftoa_leftadj   = 0x10;
244
const unsigned int OFStandard::ftoa_zeropad   = 0x20;
245
246
// --- string functions ---
247
248
#ifndef HAVE_STRLCPY
249
/*
250
 * Copy src to string dst of size siz.  At most siz-1 characters
251
 * will be copied.  Always NUL terminates (unless siz == 0).
252
 * Returns strlen(src); if retval >= siz, truncation occurred.
253
 */
254
size_t OFStandard::my_strlcpy(char *dst, const char *src, size_t siz)
255
10.7k
{
256
10.7k
  char *d = dst;
257
10.7k
  const char *s = src;
258
10.7k
  size_t n = siz;
259
260
  /* Copy as many bytes as will fit */
261
10.7k
  if (n != 0 && --n != 0)
262
8.61k
  {
263
8.61k
    do
264
99.5k
    {
265
99.5k
      if ((*d++ = *s++) == 0)
266
2.39k
         break;
267
99.5k
    } while (--n != 0);
268
8.61k
  }
269
270
  /* Not enough room in dst, add NUL and traverse rest of src */
271
10.7k
  if (n == 0)
272
8.38k
  {
273
8.38k
     if (siz != 0)
274
8.38k
        *d = '\0'; /* NUL-terminate dst */
275
8.38k
     while (*s++) /* do nothing */ ;
276
8.38k
  }
277
278
10.7k
  return(s - src - 1);    /* count does not include NUL */
279
10.7k
}
280
#endif /* HAVE_STRLCPY */
281
282
283
#ifndef HAVE_STRLCAT
284
/*
285
 * Appends src to string dst of size siz (unlike strncat, siz is the
286
 * full size of dst, not space left).  At most siz-1 characters
287
 * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
288
 * Returns strlen(src) + MIN(siz, strlen(initial dst)).
289
 * If retval >= siz, truncation occurred.
290
 */
291
size_t OFStandard::my_strlcat(char *dst, const char *src, size_t siz)
292
0
{
293
0
  char *d = dst;
294
0
  const char *s = src;
295
0
  size_t n = siz;
296
0
  size_t dlen;
297
298
  /* Find the end of dst and adjust bytes left but don't go past end */
299
0
  while (n-- != 0 && *d != '\0') d++;
300
0
  dlen = d - dst;
301
0
  n = siz - dlen;
302
303
0
  if (n == 0) return(dlen + strlen(s));
304
0
  while (*s != '\0')
305
0
  {
306
0
    if (n != 1)
307
0
    {
308
0
      *d++ = *s;
309
0
      n--;
310
0
    }
311
0
    s++;
312
0
  }
313
0
  *d = '\0';
314
315
0
  return(dlen + (s - src));       /* count does not include NUL */
316
0
}
317
#endif /* HAVE_STRLCAT */
318
319
int OFStandard::snprintf(char *str, size_t size, const char *format, ...)
320
65.0k
{
321
    // we emulate snprintf() via vsnprintf().
322
65.0k
    int count;
323
65.0k
    va_list ap;
324
65.0k
    va_start(ap, format);
325
65.0k
    count = OFStandard::vsnprintf(str, size, format, ap);
326
65.0k
    va_end(ap);
327
65.0k
    return count;
328
65.0k
}
329
330
int OFStandard::vsnprintf(char *str, size_t size, const char *format, va_list ap)
331
65.0k
{
332
#ifdef _MSC_VER
333
#if _MSC_VER < 1900
334
    // Visual Studio versions 2005 to 2013 do not have a C99 compliant
335
    // vsnprintf(), but they have _snprintf(), which can be used to emulate it.
336
    int count = -1;
337
338
    if (size != 0)
339
        count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
340
    if (count == -1)
341
        count = _vscprintf(format, ap);
342
343
    return count;
344
#else /* _MSC_VER < 1900 */
345
    // Visual Studio 2015 and newer has a C99 compliant vsnprintf().
346
    return ::vsnprintf(str, size, format, ap);
347
#endif /* _MSC_VER < 1900 */
348
#else /* _MSC_VER */
349
65.0k
    return ::vsnprintf(str, size, format, ap);
350
65.0k
#endif /* _MSC_VER */
351
65.0k
}
352
353
#ifdef HAVE_PROTOTYPE_STRERROR_R
354
/*
355
 * convert a given error code to a string. This function wraps the various
356
 * approaches found on different systems. Internally, the standard function
357
 * strerror() or strerror_r() is used.
358
 */
359
const char *OFStandard::strerror(const int errnum,
360
                                 char *buf,
361
                                 const size_t buflen)
362
0
{
363
0
    const char *result = "";
364
0
    if ((buf != NULL) && (buflen > 0))
365
0
    {
366
        // be paranoid and initialize the buffer to empty string
367
0
        buf[0] = 0;
368
        // two incompatible interfaces for strerror_r with different return types exist
369
0
#ifdef HAVE_CHARP_STRERROR_R
370
        // we're using the GNU specific version that returns the result, which may
371
        // or may not be a pointer to buf
372
0
        result = strerror_r(errnum, buf, buflen);
373
#else
374
        // we're using the X/OPEN version that always stores the result in buf
375
        (void) strerror_r(errnum, buf, buflen);
376
        result = buf;
377
#endif
378
0
    }
379
0
    return result;
380
0
}
381
#else
382
const char *OFStandard::strerror(const int errnum,
383
                                 char * /*buf*/,
384
                                 const size_t /*buflen*/)
385
{
386
    // we only have strerror() which is thread unsafe on Posix platforms, but thread safe on Windows
387
    return :: strerror(errnum);
388
}
389
#endif
390
391
392
OFString &OFStandard::toUpper(OFString &result,
393
                              const OFString &value)
394
0
{
395
0
    result = value;
396
0
    return OFStandard::toUpper(result);
397
0
}
398
399
400
OFString &OFStandard::toUpper(OFString &value)
401
0
{
402
0
    const size_t length = value.length();
403
0
    unsigned char c;
404
0
    for (size_t i = 0; i < length; i++)
405
0
    {
406
0
        c = value.at(i);
407
0
        value.at(i) = OFstatic_cast(char, toupper(c));
408
0
    }
409
0
    return value;
410
0
}
411
412
413
OFString &OFStandard::toLower(OFString &result,
414
                              const OFString &value)
415
0
{
416
0
    result = value;
417
0
    return OFStandard::toLower(result);
418
0
}
419
420
421
OFString &OFStandard::toLower(OFString &value)
422
0
{
423
0
    const size_t length = value.length();
424
0
    unsigned char c;
425
0
    for (size_t i = 0; i < length; i++)
426
0
    {
427
0
        c = value.at(i);
428
0
        value.at(i) = OFstatic_cast(char, tolower(c));
429
0
    }
430
0
    return value;
431
0
}
432
433
434
// --- file system functions ---
435
436
OFBool OFStandard::pathExists(const OFFilename &pathName)
437
1
{
438
1
    OFBool result = OFFalse;
439
    /* check for valid path name (avoid NULL or empty string) */
440
1
    if (!pathName.isEmpty())
441
1
    {
442
        /* check existence with "access()" */
443
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
444
        /* check whether to use the wide-char version of the API function */
445
        if (pathName.usesWideChars())
446
            result = (_waccess(pathName.getWideCharPointer(), F_OK) == 0);
447
        else
448
#endif
449
1
            result = (access(pathName.getCharPointer(), F_OK) == 0);
450
1
    }
451
1
    return result;
452
1
}
453
454
455
OFBool OFStandard::fileExists(const OFFilename &fileName)
456
1
{
457
1
    OFBool result = OFFalse;
458
    /* check for valid file name (avoid NULL or empty string) */
459
1
    if (!fileName.isEmpty())
460
1
    {
461
#ifdef HAVE_WINDOWS_H
462
        /* get file attributes */
463
        DWORD fileAttr;
464
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
465
        /* check whether to use the wide-char version of the API function */
466
        if (fileName.usesWideChars())
467
            fileAttr = GetFileAttributesW(fileName.getWideCharPointer());
468
        else
469
#endif
470
            fileAttr = GetFileAttributesA(fileName.getCharPointer());
471
        if (fileAttr != 0xffffffff)
472
        {
473
            /* check file type (not a directory?) */
474
            result = ((fileAttr & FILE_ATTRIBUTE_DIRECTORY) == 0);
475
        }
476
#else /* HAVE_WINDOWS_H */
477
        /* check whether path exists (but does not point to a directory) */
478
1
        result = pathExists(fileName.getCharPointer()) && !dirExists(fileName.getCharPointer());
479
1
#endif /* HAVE_WINDOWS_H */
480
1
    }
481
1
    return result;
482
1
}
483
484
485
OFBool OFStandard::dirExists(const OFFilename &dirName)
486
0
{
487
0
    OFBool result = OFFalse;
488
    /* check for valid directory name (avoid NULL or empty string) */
489
0
    if (!dirName.isEmpty())
490
0
    {
491
#ifdef HAVE_WINDOWS_H
492
        /* get file attributes of the directory */
493
        DWORD fileAttr;
494
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
495
        /* check whether to use the wide-char version of the API function */
496
        if (dirName.usesWideChars())
497
            fileAttr = GetFileAttributesW(dirName.getWideCharPointer());
498
        else
499
#endif
500
            fileAttr = GetFileAttributesA(dirName.getCharPointer());
501
        if (fileAttr != 0xffffffff)
502
        {
503
            /* check file type (is a directory?) */
504
            result = ((fileAttr & FILE_ATTRIBUTE_DIRECTORY) != 0);
505
        }
506
#else /* HAVE_WINDOWS_H */
507
        /* try to open the given directory */
508
0
        DIR *dirPtr = opendir(dirName.getCharPointer());
509
0
        if (dirPtr != NULL)
510
0
        {
511
0
            result = OFTrue;
512
0
            closedir(dirPtr);
513
0
        }
514
0
#endif /* HAVE_WINDOWS_H */
515
0
    }
516
0
    return result;
517
0
}
518
519
520
OFBool OFStandard::isReadable(const OFFilename &pathName)
521
0
{
522
0
    OFBool result = OFFalse;
523
    /* check for valid path name (avoid NULL or empty string) */
524
0
    if (!pathName.isEmpty())
525
0
    {
526
        /* check whether the path is readable using "access()" */
527
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
528
        /* check whether to use the wide-char version of the API function */
529
        if (pathName.usesWideChars())
530
            result = (_waccess(pathName.getWideCharPointer(), R_OK) == 0);
531
        else
532
#endif
533
0
            result = (access(pathName.getCharPointer(), R_OK) == 0);
534
0
}
535
0
    return result;
536
0
}
537
538
539
OFBool OFStandard::isWriteable(const OFFilename &pathName)
540
0
{
541
0
    OFBool result = OFFalse;
542
    /* check for valid path name (avoid NULL or empty string) */
543
0
    if (!pathName.isEmpty())
544
0
    {
545
        /* check whether the path is writable using "access()" */
546
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
547
        /* check whether to use the wide-char version of the API function */
548
        if (pathName.usesWideChars())
549
            result = (_waccess(pathName.getWideCharPointer(), W_OK) == 0);
550
        else
551
#endif
552
0
            result = (access(pathName.getCharPointer(), W_OK) == 0);
553
0
    }
554
0
    return result;
555
0
}
556
557
558
OFString &OFStandard::getDirNameFromPath(OFString &result,
559
                                         const OFString &pathName,
560
                                         const OFBool assumeDirName)
561
0
{
562
0
    OFFilename resultFilename;
563
    /* call the real function */
564
0
    getDirNameFromPath(resultFilename, pathName, assumeDirName);
565
    /* convert result into a string object */
566
0
    result = OFSTRING_GUARD(resultFilename.getCharPointer());
567
0
    return result;
568
0
}
569
570
571
OFFilename &OFStandard::getDirNameFromPath(OFFilename &result,
572
                                           const OFFilename &pathName,
573
                                           const OFBool assumeDirName)
574
0
{
575
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
576
    /* check whether to use the wide-char version of the API function */
577
    if (pathName.usesWideChars())
578
    {
579
        const wchar_t *strValue = pathName.getWideCharPointer();
580
        const wchar_t *strPos = wcsrchr(strValue, L'\\' /* WIDE_PATH_SEPARATOR */);
581
582
        // Windows accepts both backslash and forward slash as path separators.
583
        const wchar_t *strPos2 = wcsrchr(strValue, L'/');
584
585
        // if strPos2 points to a character closer to the end of the string, use this instead of strPos
586
        if ((strPos == NULL) || ((strPos2 != NULL) && (strPos2 > strPos))) strPos = strPos2;
587
588
        /* path separator found? */
589
        if (strPos == NULL)
590
        {
591
            if (assumeDirName)
592
                result = pathName;
593
            else
594
                result.clear();
595
        } else {
596
            wchar_t *tmpString = new wchar_t[strPos - strValue + 1];
597
            wcsncpy(tmpString, strValue, strPos - strValue);
598
            tmpString[strPos - strValue] = L'\0';
599
            result.set(tmpString, OFTrue /*convert*/);
600
            delete[] tmpString;
601
        }
602
    } else
603
#endif
604
    /* otherwise, use the conventional 8-bit characters version */
605
0
    {
606
0
        const char *strValue = pathName.getCharPointer();
607
0
        const char *strPos = strrchr(strValue, PATH_SEPARATOR);
608
609
#ifdef _WIN32
610
        // Windows accepts both backslash and forward slash as path separators.
611
        const char *strPos2 = strrchr(strValue, '/');
612
613
        // if strPos2 points to a character closer to the end of the string, use this instead of strPos
614
        if ((strPos == NULL) || ((strPos2 != NULL) && (strPos2 > strPos))) strPos = strPos2;
615
#endif
616
617
        /* path separator found? */
618
0
        if (strPos == NULL)
619
0
        {
620
0
            if (assumeDirName)
621
0
                result = pathName;
622
0
            else
623
0
                result.clear();
624
0
        } else
625
0
            result.set(OFString(strValue, strPos - strValue));
626
0
    }
627
0
    return result;
628
0
}
629
630
631
OFString &OFStandard::getFilenameFromPath(OFString &result,
632
                                          const OFString &pathName,
633
                                          const OFBool assumeFilename)
634
0
{
635
0
    OFFilename resultFilename;
636
    /* call the real function */
637
0
    getFilenameFromPath(resultFilename, pathName, assumeFilename);
638
    /* convert result into a string object */
639
0
    result = OFSTRING_GUARD(resultFilename.getCharPointer());
640
0
    return result;
641
0
}
642
643
644
OFFilename &OFStandard::getFilenameFromPath(OFFilename &result,
645
                                            const OFFilename &pathName,
646
                                            const OFBool assumeFilename)
647
0
{
648
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
649
    /* check whether to use the wide-char version of the API function */
650
    if (pathName.usesWideChars())
651
    {
652
        const wchar_t *strValue = pathName.getWideCharPointer();
653
        const wchar_t *strPos = wcsrchr(strValue, L'\\' /* WIDE_PATH_SEPARATOR */);
654
655
        // Windows accepts both backslash and forward slash as path separators.
656
        const wchar_t *strPos2 = wcsrchr(strValue, L'/');
657
658
        // if strPos2 points to a character closer to the end of the string, use this instead of strPos
659
        if ((strPos == NULL) || ((strPos2 != NULL) && (strPos2 > strPos))) strPos = strPos2;
660
661
        /* path separator found? */
662
        if (strPos == NULL)
663
        {
664
            if (assumeFilename)
665
                result = pathName;
666
            else
667
                result.clear();
668
        } else {
669
            wchar_t *tmpString = new wchar_t[wcslen(strPos)];
670
            wcscpy(tmpString, strPos + 1);
671
            result.set(tmpString, OFTrue /*convert*/);
672
            delete[] tmpString;
673
        }
674
    } else
675
#endif
676
    /* otherwise, use the conventional 8-bit characters version */
677
0
    {
678
0
        const char *strValue = pathName.getCharPointer();
679
0
        const char *strPos = strrchr(strValue, PATH_SEPARATOR);
680
681
#ifdef _WIN32
682
        // Windows accepts both backslash and forward slash as path separators.
683
        const char *strPos2 = strrchr(strValue, '/');
684
685
        // if strPos2 points to a character closer to the end of the string, use this instead of strPos
686
        if ((strPos == NULL) || ((strPos2 != NULL) && (strPos2 > strPos))) strPos = strPos2;
687
#endif
688
689
        /* path separator found? */
690
0
        if (strPos == NULL)
691
0
        {
692
0
            if (assumeFilename)
693
0
                result = pathName;
694
0
            else
695
0
                result.clear();
696
0
        } else
697
0
            result.set(OFString(strPos + 1));
698
0
    }
699
0
    return result;
700
0
}
701
702
703
OFString &OFStandard::normalizeDirName(OFString &result,
704
                                       const OFString &dirName,
705
                                       const OFBool allowEmptyDirName)
706
0
{
707
0
    OFFilename resultFilename;
708
    /* call the real function */
709
0
    normalizeDirName(resultFilename, dirName, allowEmptyDirName);
710
    /* convert result into a string object */
711
0
    result = OFSTRING_GUARD(resultFilename.getCharPointer());
712
0
    return result;
713
0
}
714
715
716
OFFilename &OFStandard::normalizeDirName(OFFilename &result,
717
                                         const OFFilename &dirName,
718
                                         const OFBool allowEmptyDirName)
719
0
{
720
    /* remove trailing path separators (keep it if appearing at the beginning of the string) */
721
    /* TODO: do we need to check for absolute path containing Windows drive name, e.g. "c:\"? */
722
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
723
    /* check whether to use the wide-char version of the API function */
724
    if (dirName.usesWideChars())
725
    {
726
        const wchar_t *strValue = dirName.getWideCharPointer();
727
        size_t strLength = (strValue == NULL) ? 0 : wcslen(strValue);
728
        // Windows accepts both backslash and forward slash as path separators.
729
        while ((strLength > 1) && ((strValue[strLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */) ||
730
              (strValue[strLength - 1] == L'/' )))
731
            --strLength;
732
        /* avoid "." as a directory name, use empty string instead */
733
        if (allowEmptyDirName && ((strLength == 0) || ((strLength == 1) && (strValue[0] == L'.'))))
734
            result.clear();
735
        /* avoid empty directory name (use "." instead) */
736
        else if (!allowEmptyDirName && (strLength == 0))
737
            result.set(L".", OFTrue /*convert*/);
738
        /* copy resulting string (omit trailing backslashes) */
739
        else {
740
            wchar_t *tmpString = new wchar_t[strLength + 1];
741
            wcsncpy(tmpString, strValue, strLength);
742
            tmpString[strLength] = L'\0';
743
            result.set(tmpString, OFTrue /*convert*/);
744
            delete[] tmpString;
745
        }
746
    } else
747
#endif
748
    /* otherwise, use the conventional 8-bit characters version */
749
0
    {
750
0
        const char *strValue = dirName.getCharPointer();
751
0
        size_t strLength = (strValue == NULL) ? 0 : strlen(strValue);
752
#ifdef _WIN32
753
        // Windows accepts both backslash and forward slash as path separators.
754
        while ((strLength > 1) && ((strValue[strLength - 1] == PATH_SEPARATOR) ||
755
              (strValue[strLength - 1] == '/' )))
756
            --strLength;
757
#else
758
0
        while ((strLength > 1) && (strValue[strLength - 1] == PATH_SEPARATOR))
759
0
            --strLength;
760
0
#endif
761
        /* avoid "." as a directory name, use empty string instead */
762
0
        if (allowEmptyDirName && ((strLength == 0) || ((strLength == 1) && (strValue[0] == '.'))))
763
0
            result.clear();
764
        /* avoid empty directory name (use "." instead) */
765
0
        else if (!allowEmptyDirName && (strLength == 0))
766
0
            result.set(".");
767
        /* copy resulting string (omit trailing backslashes) */
768
0
        else
769
0
            result.set(OFString(strValue, strLength));
770
0
    }
771
0
    return result;
772
0
}
773
774
775
OFString &OFStandard::combineDirAndFilename(OFString &result,
776
                                            const OFString &dirName,
777
                                            const OFString &fileName,
778
                                            const OFBool allowEmptyDirName)
779
0
{
780
0
    OFFilename resultFilename;
781
    /* call the real function */
782
0
    combineDirAndFilename(resultFilename, dirName, fileName, allowEmptyDirName);
783
    /* convert result into a string object */
784
0
    result = OFSTRING_GUARD(resultFilename.getCharPointer());
785
0
    return result;
786
0
}
787
788
789
OFFilename &OFStandard::combineDirAndFilename(OFFilename &result,
790
                                              const OFFilename &dirName,
791
                                              const OFFilename &fileName,
792
                                              const OFBool allowEmptyDirName)
793
0
{
794
    // # might use system function realpath() in the future to resolve paths including ".."
795
    // # or combinations of absolute paths in both 'dirName' and 'fileName'
796
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
797
    /* check whether to use the wide-char version of the API function */
798
    if (dirName.usesWideChars() || fileName.usesWideChars())
799
    {
800
        const wchar_t *strValue = fileName.getWideCharPointer();
801
        size_t strLength = (strValue == NULL) ? 0 : wcslen(strValue);
802
        /* check whether 'fileName' contains absolute path */
803
        /* (this check also covers UNC syntax, e.g. "\\server\...") */
804
        // Windows accepts both backslash and forward slash as path separators.
805
        if ((strLength > 0) && ((strValue[0] == L'\\' /* WIDE_PATH_SEPARATOR */) || (strValue[0] == L'/')))
806
        {
807
            result.set(strValue, OFTrue /*convert*/);
808
            return result;
809
        }
810
#ifdef HAVE_WINDOWS_H
811
        else if (strLength >= 3)
812
        {
813
            /* check for absolute path containing Windows drive name, e.g. "c:\..." */
814
            const wchar_t c = strValue[0];
815
            if (((c >= L'A') && (c <= L'Z')) || ((c >= L'a') && (c <= L'z')))
816
            {
817
                // Windows accepts both backslash and forward slash as path separators.
818
                if ((strValue[1] == L':') && ((strValue[2] == L'\\' /* WIDE_PATH_SEPARATOR */))||(strValue[2] == L'/'))
819
                {
820
                    result.set(strValue, OFTrue /*convert*/);
821
                    return result;
822
                }
823
            }
824
        }
825
#endif
826
        /* we only get here, if we don't have an absolute directory in "fileName" */
827
        /* now normalize the directory name */
828
        normalizeDirName(result, dirName, allowEmptyDirName);
829
        /* do some extra checks on a special case */
830
        if (!result.isEmpty() && !result.usesWideChars())
831
        {
832
            /* make sure that wide-char version exists */
833
            OFFilename tmpDirName(result);
834
            result.set(tmpDirName.getCharPointer(), OFTrue /*convert*/);
835
        }
836
        /* check file name (ignore empty string and ".") */
837
        if ((strLength > 1) || ((strLength == 1) && (strValue[0] != L'.')))
838
        {
839
            if (result.isEmpty())
840
                result.set(strValue, OFTrue /*convert*/);
841
            else {
842
                const wchar_t *resValue = result.getWideCharPointer();
843
                const size_t resLength = wcslen(resValue); /* should never be 0 */
844
                wchar_t *tmpString = new wchar_t[strLength + resLength + 1 + 1];
845
                wcscpy(tmpString, resValue);
846
                /* add path separator (if required) ... */
847
                // Windows accepts both backslash and forward slash as path separators.
848
                if ((resValue[resLength - 1] != L'\\' /* WIDE_PATH_SEPARATOR */) && (resValue[resLength - 1] != L'/'))
849
                {
850
                    tmpString[resLength] = L'\\' /* WIDE_PATH_SEPARATOR */;
851
                    tmpString[resLength + 1] = L'\0';
852
                }
853
                /* ...and file name */
854
                wcscat(tmpString, strValue);
855
                result.set(tmpString, OFTrue /*convert*/);
856
                delete[] tmpString;
857
            }
858
        }
859
    } else
860
#endif
861
    /* otherwise, use the conventional 8-bit characters version */
862
0
    {
863
0
        const char *strValue = fileName.getCharPointer();
864
0
        size_t strLength = (strValue == NULL) ? 0 : strlen(strValue);
865
        /* check whether 'fileName' contains absolute path */
866
        /* (this check also covers UNC syntax, e.g. "\\server\...") */
867
#ifdef _WIN32
868
        // Windows accepts both backslash and forward slash as path separators.
869
        if ((strLength > 0) && ((strValue[0] == PATH_SEPARATOR) || (strValue[0] == '/')))
870
#else
871
0
        if ((strLength > 0) && (strValue[0] == PATH_SEPARATOR))
872
0
#endif
873
0
        {
874
0
            result.set(strValue);
875
0
            return result;
876
0
        }
877
#ifdef HAVE_WINDOWS_H
878
        else if (strLength >= 3)
879
        {
880
            /* check for absolute path containing Windows drive name, e.g. "c:\..." */
881
            const char c = strValue[0];
882
            if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')))
883
            {
884
                if ((strValue[1] == ':') && ((strValue[2] == '\\') || (strValue[2] == '/')))
885
                {
886
                    result.set(strValue);
887
                    return result;
888
                }
889
            }
890
        }
891
#endif
892
        /* we only get here, if we don't have an absolute directory in "fileName" */
893
        /* now normalize the directory name */
894
0
        normalizeDirName(result, dirName, allowEmptyDirName);
895
        /* check file name (ignore empty string and ".") */
896
0
        if ((strLength > 1) || ((strLength == 1) && (strValue[0] != '.')))
897
0
        {
898
0
            if (result.isEmpty())
899
0
                result.set(strValue);
900
0
            else {
901
0
                const char *resValue = result.getCharPointer();
902
0
                const size_t resLength = strlen(resValue); /* should never be 0 */
903
0
                const size_t buflen = strLength + resLength + 1 + 1;
904
0
                char *tmpString = new char[buflen];
905
0
                OFStandard::strlcpy(tmpString, resValue, buflen);
906
                /* add path separator (if required) ... */
907
#ifdef _WIN32
908
                // Windows accepts both backslash and forward slash as path separators.
909
                if ((resValue[resLength - 1] != PATH_SEPARATOR) && (resValue[resLength - 1] != '/'))
910
#else
911
0
                if (resValue[resLength - 1] != PATH_SEPARATOR)
912
0
#endif
913
0
                {
914
0
                    tmpString[resLength] = PATH_SEPARATOR;
915
0
                    tmpString[resLength + 1] = '\0';
916
0
                }
917
                /* ...and file name */
918
0
                OFStandard::strlcat(tmpString, strValue, buflen);
919
0
                result.set(tmpString);
920
0
                delete[] tmpString;
921
0
            }
922
0
        }
923
0
    }
924
0
    return result;
925
0
}
926
927
928
OFCondition OFStandard::removeRootDirFromPathname(OFFilename &result,
929
                                                  const OFFilename &rootDir,
930
                                                  const OFFilename &pathName,
931
                                                  const OFBool allowLeadingPathSeparator)
932
0
{
933
0
    OFCondition status = EC_IllegalParameter;
934
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
935
    /* check whether to use the wide-char version of the API function */
936
    if (rootDir.usesWideChars() || pathName.usesWideChars())
937
    {
938
        const wchar_t *rootValue = rootDir.getWideCharPointer();
939
        const wchar_t *pathValue = pathName.getWideCharPointer();
940
        const size_t rootLength = (rootValue == NULL) ? 0 : wcslen(rootValue);
941
        const size_t pathLength = (pathValue == NULL) ? 0 : wcslen(pathValue);
942
        /* check for empty strings */
943
        if ((rootLength == 0) && (pathLength == 0))
944
        {
945
            result.set("", OFTrue /*convert*/);
946
            status = EC_Normal;
947
        }
948
        /* check for empty root dir */
949
        else if (rootLength == 0)
950
        {
951
            result.set(pathValue, OFTrue /*convert*/);
952
            status = EC_Normal;
953
        }
954
        /* check for "compatible" length */
955
        else if (rootLength <= pathLength)
956
        {
957
            /* check for same prefix */
958
            if (wcsncmp(rootValue, pathValue, rootLength) == 0)
959
            {
960
                /* create temporary buffer for destination string */
961
                wchar_t *tmpString = new wchar_t[pathLength - rootLength + 1];
962
                /* remove root dir prefix from path name */
963
                wcscpy(tmpString, pathValue + rootLength);
964
                /* remove leading path separator (if present) */
965
                if (!allowLeadingPathSeparator && ((tmpString[0] == PATH_SEPARATOR) || (tmpString[0] == '/')))
966
                    result.set(tmpString + 1, OFTrue /*convert*/);
967
                else
968
                    result.set(tmpString, OFTrue /*convert*/);
969
                delete[] tmpString;
970
                status = EC_Normal;
971
            }
972
        }
973
    } else
974
#endif
975
    /* otherwise, use the conventional 8-bit characters version */
976
0
    {
977
0
        const char *rootValue = rootDir.getCharPointer();
978
0
        const char *pathValue = pathName.getCharPointer();
979
0
        const size_t rootLength = (rootValue == NULL) ? 0 : strlen(rootValue);
980
0
        const size_t pathLength = (pathValue == NULL) ? 0 : strlen(pathValue);
981
        /* check for empty strings */
982
0
        if ((rootLength == 0) && (pathLength == 0))
983
0
        {
984
0
            result.set("");
985
0
            status = EC_Normal;
986
0
        }
987
        /* check for empty root dir */
988
0
        else if (rootLength == 0)
989
0
        {
990
0
            result.set(pathValue);
991
0
            status = EC_Normal;
992
0
        }
993
        /* check for "compatible" length */
994
0
        else if (rootLength <= pathLength)
995
0
        {
996
            /* check for same prefix */
997
0
            if (strncmp(rootValue, pathValue, rootLength) == 0)
998
0
            {
999
                /* create temporary buffer for destination string */
1000
0
                size_t buflen = pathLength - rootLength + 1;
1001
0
                char *tmpString = new char[buflen];
1002
                /* remove root dir prefix from path name */
1003
0
                OFStandard::strlcpy(tmpString, pathValue + rootLength, buflen);
1004
                /* remove leading path separator (if present) */
1005
#ifdef _WIN32
1006
                // Windows accepts both backslash and forward slash as path separators.
1007
                if (!allowLeadingPathSeparator && ((tmpString[0] == PATH_SEPARATOR) || (tmpString[0] == '/')))
1008
#else
1009
0
                if (!allowLeadingPathSeparator && (tmpString[0] == PATH_SEPARATOR))
1010
0
#endif
1011
0
                    result.set(tmpString + 1);
1012
0
                else
1013
0
                    result.set(tmpString);
1014
0
                delete[] tmpString;
1015
0
                status = EC_Normal;
1016
0
            }
1017
0
        }
1018
0
    }
1019
    /* return empty string in case of error */
1020
0
    if (status.bad())
1021
0
        result.clear();
1022
0
    return status;
1023
0
}
1024
1025
1026
OFFilename &OFStandard::appendFilenameExtension(OFFilename &result,
1027
                                                const OFFilename &fileName,
1028
                                                const OFFilename &fileExtension)
1029
0
{
1030
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
1031
    /* check whether to use the wide-char version of the API function */
1032
    if (fileName.usesWideChars())
1033
    {
1034
        OFFilename fileExt(fileExtension);
1035
        /* convert file extension to wide chars (if needed) */
1036
        if (!fileExt.isEmpty() && !fileExt.usesWideChars())
1037
            fileExt.set(fileExtension.getCharPointer(), OFTrue /*convert*/);
1038
        const wchar_t *namValue = fileName.getWideCharPointer();
1039
        const wchar_t *extValue = fileExt.getWideCharPointer();
1040
        size_t namLength = (namValue == NULL) ? 0 : wcslen(namValue);
1041
        size_t extLength = (extValue == NULL) ? 0 : wcslen(extValue);
1042
        /* create temporary buffer for destination string */
1043
        wchar_t *tmpString = new wchar_t[namLength + extLength + 1];
1044
        wcscpy(tmpString, namValue);
1045
        if (extValue != NULL)
1046
            wcscat(tmpString, extValue);
1047
        result.set(tmpString, OFTrue /*convert*/);
1048
        delete[] tmpString;
1049
    } else
1050
#endif
1051
    /* otherwise, use the conventional 8-bit characters version */
1052
0
    {
1053
0
        const char *namValue = fileName.getCharPointer();
1054
0
        const char *extValue = fileExtension.getCharPointer();
1055
0
        size_t namLength = (namValue == NULL) ? 0 : strlen(namValue);
1056
0
        size_t extLength = (extValue == NULL) ? 0 : strlen(extValue);
1057
        /* create temporary buffer for destination string */
1058
0
        size_t buflen = namLength + extLength + 1;
1059
0
        char *tmpString = new char[buflen];
1060
0
        OFStandard::strlcpy(tmpString, (namValue == NULL) ? "" : namValue, buflen);
1061
0
        if (extValue != NULL)
1062
0
            OFStandard::strlcat(tmpString, extValue, buflen);
1063
0
        result.set(tmpString);
1064
0
        delete[] tmpString;
1065
0
    }
1066
0
    return result;
1067
0
}
1068
1069
1070
size_t OFStandard::searchDirectoryRecursively(const OFString &directory,
1071
                                              OFList<OFString> &fileList,
1072
                                              const OFString &pattern,
1073
                                              const OFString &dirPrefix,
1074
                                              const OFBool recurse)
1075
0
{
1076
0
    OFList<OFFilename> filenameList;
1077
    /* call the real function */
1078
0
    const size_t result = searchDirectoryRecursively(directory, filenameList, pattern, dirPrefix, recurse);
1079
    /* copy all list entries to reference parameter */
1080
0
    OFListIterator(OFFilename) iter = filenameList.begin();
1081
0
    OFListIterator(OFFilename) last = filenameList.end();
1082
0
    while (iter != last)
1083
0
    {
1084
0
        fileList.push_back(OFSTRING_GUARD((*iter).getCharPointer()));
1085
0
        ++iter;
1086
0
    }
1087
0
    return result;
1088
0
}
1089
1090
1091
size_t OFStandard::searchDirectoryRecursively(const OFFilename &directory,
1092
                                              OFList<OFFilename> &fileList,
1093
                                              const OFFilename &pattern,
1094
                                              const OFFilename &dirPrefix,
1095
                                              const OFBool recurse)
1096
0
{
1097
0
    const size_t initialSize = fileList.size();
1098
0
    OFFilename dirName, pathName, tmpString;
1099
0
    combineDirAndFilename(dirName, dirPrefix, directory);
1100
#ifdef HAVE_WINDOWS_H
1101
    /* check whether given directory exists */
1102
    if (dirExists(dirName))
1103
    {
1104
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
1105
        /* check whether to use the wide-char version of the API function */
1106
        if (dirName.usesWideChars())
1107
        {
1108
            HANDLE handle;
1109
            WIN32_FIND_DATAW data;
1110
            /* check whether file pattern is given */
1111
            if (!pattern.isEmpty())
1112
            {
1113
                /* first, search for matching files on this directory level */
1114
                handle = FindFirstFileW(combineDirAndFilename(tmpString, dirName, pattern, OFTrue /*allowEmptyDirName*/).getWideCharPointer(), &data);
1115
                if (handle != INVALID_HANDLE_VALUE)
1116
                {
1117
                    do {
1118
                        /* avoid leading "." */
1119
                        if (wcscmp(dirName.getWideCharPointer(), L".") == 0)
1120
                            pathName.set(data.cFileName, OFTrue /*convert*/);
1121
                        else
1122
                            combineDirAndFilename(pathName, directory, data.cFileName, OFTrue /*allowEmptyDirName*/);
1123
                        /* ignore directories and the like */
1124
                        if (fileExists(combineDirAndFilename(tmpString, dirPrefix, pathName, OFTrue /*allowEmptyDirName*/)))
1125
                            fileList.push_back(pathName);
1126
                    } while (FindNextFileW(handle, &data));
1127
                    FindClose(handle);
1128
                }
1129
            }
1130
            /* then search for _any_ file/directory entry */
1131
            handle = FindFirstFileW(combineDirAndFilename(tmpString, dirName, L"*.*", OFTrue /*allowEmptyDirName*/).getWideCharPointer(), &data);
1132
            if (handle != INVALID_HANDLE_VALUE)
1133
            {
1134
                do {
1135
                    /* filter out current and parent directory */
1136
                    if ((wcscmp(data.cFileName, L".") != 0) && (wcscmp(data.cFileName, L"..") != 0))
1137
                    {
1138
                        /* avoid leading "." */
1139
                        if (wcscmp(dirName.getWideCharPointer(), L".") == 0)
1140
                            pathName.set(data.cFileName, OFTrue /*convert*/);
1141
                        else
1142
                            combineDirAndFilename(pathName, directory, data.cFileName, OFTrue /*allowEmptyDirName*/);
1143
                        if (dirExists(combineDirAndFilename(tmpString, dirPrefix, pathName, OFTrue /*allowEmptyDirName*/)))
1144
                        {
1145
                            /* recursively search sub directories */
1146
                            if (recurse)
1147
                                searchDirectoryRecursively(pathName, fileList, pattern, dirPrefix, recurse);
1148
                        }
1149
                        else if (pattern.isEmpty())
1150
                        {
1151
                            /* add filename to the list (if no pattern is given) */
1152
                            fileList.push_back(pathName);
1153
                        }
1154
                    }
1155
                } while (FindNextFileW(handle, &data));
1156
                FindClose(handle);
1157
            }
1158
        } else
1159
#endif /* defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32) */
1160
        /* otherwise, use the conventional 8-bit characters version */
1161
        {
1162
            HANDLE handle;
1163
            WIN32_FIND_DATAA data;
1164
            /* check whether file pattern is given */
1165
            if (!pattern.isEmpty())
1166
            {
1167
                /* first, search for matching files on this directory level */
1168
                handle = FindFirstFileA(combineDirAndFilename(tmpString, dirName, pattern, OFTrue /*allowEmptyDirName*/).getCharPointer(), &data);
1169
                if (handle != INVALID_HANDLE_VALUE)
1170
                {
1171
                    do {
1172
                        /* avoid leading "." */
1173
                        if (strcmp(dirName.getCharPointer(), ".") == 0)
1174
                            pathName.set(data.cFileName);
1175
                        else
1176
                            combineDirAndFilename(pathName, directory, data.cFileName, OFTrue /*allowEmptyDirName*/);
1177
                        /* ignore directories and the like */
1178
                        if (fileExists(combineDirAndFilename(tmpString, dirPrefix, pathName, OFTrue /*allowEmptyDirName*/)))
1179
                            fileList.push_back(pathName);
1180
                    } while (FindNextFileA(handle, &data));
1181
                    FindClose(handle);
1182
                }
1183
            }
1184
            /* then search for _any_ file/directory entry */
1185
            handle = FindFirstFileA(combineDirAndFilename(tmpString, dirName, "*.*", OFTrue /*allowEmptyDirName*/).getCharPointer(), &data);
1186
            if (handle != INVALID_HANDLE_VALUE)
1187
            {
1188
                do {
1189
                    /* filter out current and parent directory */
1190
                    if ((strcmp(data.cFileName, ".") != 0) && (strcmp(data.cFileName, "..") != 0))
1191
                    {
1192
                        /* avoid leading "." */
1193
                        if (strcmp(dirName.getCharPointer(), ".") == 0)
1194
                            pathName.set(data.cFileName);
1195
                        else
1196
                            combineDirAndFilename(pathName, directory, data.cFileName, OFTrue /*allowEmptyDirName*/);
1197
                        if (dirExists(combineDirAndFilename(tmpString, dirPrefix, pathName, OFTrue /*allowEmptyDirName*/)))
1198
                        {
1199
                            /* recursively search sub directories */
1200
                            if (recurse)
1201
                                searchDirectoryRecursively(pathName, fileList, pattern, dirPrefix, recurse);
1202
                        }
1203
                        else if (pattern.isEmpty())
1204
                        {
1205
                            /* add filename to the list (if no pattern is given) */
1206
                            fileList.push_back(pathName);
1207
                        }
1208
                    }
1209
                } while (FindNextFileA(handle, &data));
1210
                FindClose(handle);
1211
            }
1212
        }
1213
    }
1214
#else /* HAVE_WINDOWS_H */
1215
    /* try to open the directory */
1216
0
    DIR *dirPtr = opendir(dirName.getCharPointer());
1217
0
    if (dirPtr != NULL)
1218
0
    {
1219
0
        struct dirent *entry = NULL;
1220
#if defined(HAVE_READDIR_R) && !defined(READDIR_IS_THREADSAFE)
1221
        dirent d = {};
1222
        while (!readdir_r(dirPtr, &d, &entry) && entry)
1223
#else
1224
0
        while ((entry = readdir(dirPtr)) != NULL)
1225
0
#endif
1226
0
        {
1227
            /* filter out current (".") and parent directory ("..") */
1228
0
            if ((strcmp(entry->d_name, ".") != 0) && (strcmp(entry->d_name, "..") != 0))
1229
0
            {
1230
                /* avoid leading "." */
1231
0
                if (strcmp(dirName.getCharPointer(), ".") == 0)
1232
0
                    pathName = entry->d_name;
1233
0
                else
1234
0
                    combineDirAndFilename(pathName, directory, entry->d_name, OFTrue /*allowEmptyDirName*/);
1235
0
                if (dirExists(combineDirAndFilename(tmpString, dirPrefix, pathName, OFTrue /*allowEmptyDirName*/)))
1236
0
                {
1237
                    /* recursively search sub directories */
1238
0
                    if (recurse)
1239
0
                        searchDirectoryRecursively(pathName, fileList, pattern, dirPrefix, recurse);
1240
0
                } else {
1241
0
#ifdef HAVE_FNMATCH_H
1242
                    /* check whether filename matches pattern */
1243
0
                    if ((pattern.isEmpty()) || (fnmatch(pattern.getCharPointer(), entry->d_name, FNM_PATHNAME) == 0))
1244
#else
1245
                        /* no pattern matching, sorry :-/ */
1246
#endif
1247
0
                        fileList.push_back(pathName);
1248
0
                }
1249
0
            }
1250
0
        }
1251
0
        closedir(dirPtr);
1252
0
    }
1253
0
#endif /* HAVE_WINDOWS_H */
1254
    /* return number of added files */
1255
0
    return fileList.size() - initialSize;
1256
0
}
1257
1258
1259
OFCondition OFStandard::createDirectory(const OFFilename &dirName,
1260
                                        const OFFilename &rootDir)
1261
0
{
1262
0
    OFCondition status = EC_Normal;
1263
    /* first, check whether the directory already exists */
1264
0
    if (!dirExists(dirName))
1265
0
    {
1266
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
1267
        /* check whether to use the wide-char version of the API function */
1268
        if (dirName.usesWideChars())
1269
        {
1270
            /* then, check whether the given prefix can be skipped */
1271
            size_t pos = 0;
1272
            const wchar_t *dirValue = dirName.getWideCharPointer();
1273
            const wchar_t *rootValue = rootDir.getWideCharPointer();
1274
            size_t dirLength = (dirValue == NULL) ? 0 : wcslen(dirValue);
1275
            size_t rootLength = (rootValue == NULL) ? 0 : wcslen(rootValue);
1276
            /* check for absolute path containing Windows drive name, e. g. "c:\",
1277
             * is not required since the root directory should always exist */
1278
#ifdef _WIN32
1279
            // Windows accepts both backslash and forward slash as path separators.
1280
            if ((dirLength > 1) && ((dirValue[dirLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */) || (dirValue[dirLength - 1] == L'/')))
1281
#else
1282
            if ((dirLength > 1) && (dirValue[dirLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */))
1283
#endif
1284
            {
1285
                /* ignore trailing path separator */
1286
                --dirLength;
1287
            }
1288
#ifdef _WIN32
1289
            // Windows accepts both backslash and forward slash as path separators.
1290
            if ((rootLength > 1) && ((rootValue[rootLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */) || (rootValue[rootLength - 1] == L'/')))
1291
#else
1292
            if ((rootLength > 1) && (rootValue[rootLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */))
1293
#endif
1294
            {
1295
                /* ignore trailing path separator */
1296
                --rootLength;
1297
            }
1298
            /* check for "compatible" length */
1299
            if ((rootLength > 0) && (rootLength < dirLength))
1300
            {
1301
                /* check for common prefix */
1302
                if (wcsncmp(dirValue, rootValue, rootLength) == 0)
1303
                {
1304
                    /* check whether root directory really exists */
1305
                    if (dirExists(rootDir))
1306
                    {
1307
                        /* start searching after the common prefix */
1308
                        pos = rootLength;
1309
                    }
1310
                }
1311
            }
1312
            /* and finally, iterate over all subsequent subdirectories */
1313
            do {
1314
                /* search for next path separator */
1315
                do {
1316
                    ++pos;
1317
#ifdef _WIN32
1318
                // Windows accepts both backslash and forward slash as path separators.
1319
                } while ((dirValue[pos] != L'\\' /* WIDE_PATH_SEPARATOR */) && (dirValue[pos] != L'/') && (dirValue[pos] != '\0'));
1320
#else
1321
                } while ((dirValue[pos] != L'\\' /* WIDE_PATH_SEPARATOR */) && (dirValue[pos] != L'\0'));
1322
#endif
1323
1324
                /* get name of current directory component */
1325
                wchar_t *subDir = new wchar_t[pos + 1];
1326
                wcsncpy(subDir, dirValue, pos /*num*/);
1327
                subDir[pos] = L'\0';
1328
                if (!dirExists(subDir))
1329
                {
1330
                    /* and create the directory component (if not already existing) */
1331
                    if (_wmkdir(subDir) == -1)
1332
                    {
1333
                        char errBuf[256];
1334
                        OFString message("Cannot create directory: ");
1335
                        message.append(strerror(errno, errBuf, sizeof(errBuf)));
1336
                        status = makeOFCondition(0, EC_CODE_CannotCreateDirectory, OF_error, message.c_str());
1337
                        /* exit the loop */
1338
                        break;
1339
                    }
1340
                }
1341
                delete[] subDir;
1342
            } while (pos < dirLength);
1343
        } else
1344
#endif
1345
        /* otherwise, use the conventional 8-bit characters version */
1346
0
        {
1347
            /* then, check whether the given prefix can be skipped */
1348
0
            size_t pos = 0;
1349
0
            const char *dirValue = dirName.getCharPointer();
1350
0
            const char *rootValue = rootDir.getCharPointer();
1351
0
            size_t dirLength = (dirValue == NULL) ? 0 : strlen(dirValue);
1352
0
            size_t rootLength = (rootValue == NULL) ? 0 : strlen(rootValue);
1353
            /* check for absolute path containing Windows drive name, e. g. "c:\",
1354
             * is not required since the root directory should always exist */
1355
#ifdef _WIN32
1356
            // Windows accepts both backslash and forward slash as path separators.
1357
            if ((dirLength > 1) && ((dirValue[dirLength - 1] == PATH_SEPARATOR) || (dirValue[dirLength - 1] == '/')))
1358
#else
1359
0
            if ((dirLength > 1) && (dirValue[dirLength - 1] == PATH_SEPARATOR))
1360
0
#endif
1361
0
            {
1362
                /* ignore trailing path separator */
1363
0
                --dirLength;
1364
0
            }
1365
#ifdef _WIN32
1366
            // Windows accepts both backslash and forward slash as path separators.
1367
            if ((rootLength > 1) && ((rootValue[rootLength - 1] == PATH_SEPARATOR) || (rootValue[rootLength - 1] == '/')))
1368
#else
1369
0
            if ((rootLength > 1) && (rootValue[rootLength - 1] == PATH_SEPARATOR))
1370
0
#endif
1371
0
            {
1372
                /* ignore trailing path separator */
1373
0
                --rootLength;
1374
0
            }
1375
            /* check for "compatible" length */
1376
0
            if ((rootLength > 0) && (rootLength < dirLength))
1377
0
            {
1378
                /* check for common prefix */
1379
0
                if (strncmp(dirValue, rootValue, rootLength) == 0)
1380
0
                {
1381
                    /* check whether root directory really exists */
1382
0
                    if (dirExists(rootDir))
1383
0
                    {
1384
                        /* start searching after the common prefix */
1385
0
                        pos = rootLength;
1386
0
                    }
1387
0
                }
1388
0
            }
1389
            /* and finally, iterate over all subsequent subdirectories */
1390
0
            do {
1391
                /* search for next path separator */
1392
0
                do {
1393
0
                    ++pos;
1394
#ifdef _WIN32
1395
                // Windows accepts both backslash and forward slash as path separators.
1396
                } while ((dirValue[pos] != PATH_SEPARATOR) && (dirValue[pos] != '/') && (dirValue[pos] != '\0'));
1397
#else
1398
0
                } while ((dirValue[pos] != PATH_SEPARATOR) && (dirValue[pos] != '\0'));
1399
0
#endif
1400
                /* get name of current directory component */
1401
0
                char *subDir = new char[pos + 1];
1402
0
                strlcpy(subDir, dirValue, pos + 1 /*size*/);
1403
0
                if (!dirExists(subDir))
1404
0
                {
1405
                    /* and create the directory component (if not already existing) */
1406
#ifdef HAVE_WINDOWS_H
1407
                    if (_mkdir(subDir) == -1)
1408
#else
1409
0
                    if (mkdir(subDir, S_IRWXU | S_IRWXG | S_IRWXO) == -1)
1410
0
#endif
1411
0
                    {
1412
0
                        char errBuf[256];
1413
0
                        OFString message("Cannot create directory: ");
1414
0
                        message.append(strerror(errno, errBuf, sizeof(errBuf)));
1415
0
                        status = makeOFCondition(0, EC_CODE_CannotCreateDirectory, OF_error, message.c_str());
1416
                        /* exit the loop */
1417
0
                        break;
1418
0
                    }
1419
0
                }
1420
0
                delete[] subDir;
1421
0
            } while (pos < dirLength);
1422
0
        }
1423
0
    }
1424
0
    return status;
1425
0
}
1426
1427
1428
0
#define COPY_FILE_BUFFER_SIZE 4096
1429
1430
OFBool OFStandard::copyFile(const OFFilename &sourceFilename,
1431
                            const OFFilename &destFilename)
1432
0
{
1433
0
    OFBool status = OFFalse;
1434
    /* avoid NULL or empty string passed to fopen() */
1435
0
    if (!sourceFilename.isEmpty() && !destFilename.isEmpty())
1436
0
    {
1437
        /* open input file */
1438
0
        OFFile sourceFile;
1439
0
        if (sourceFile.fopen(sourceFilename, "rb"))
1440
0
        {
1441
            /* create output file */
1442
0
            OFFile destFile;
1443
0
            if (destFile.fopen(destFilename, "wb"))
1444
0
            {
1445
0
                size_t numRead = 0;
1446
0
                size_t numWrite = 0;
1447
0
                Uint8 buffer[COPY_FILE_BUFFER_SIZE];
1448
                /* read and write data in chunks */
1449
0
                do {
1450
0
                    numRead = sourceFile.fread(buffer, 1, COPY_FILE_BUFFER_SIZE);
1451
0
                } while ((numRead > 0) && ((numWrite = destFile.fwrite(buffer, 1, numRead)) == numRead));
1452
                /* check for any errors */
1453
0
                if ((sourceFile.error() == 0) && (destFile.error() == 0) && (destFile.fclose() == 0))
1454
0
                    status = OFTrue;
1455
0
            }
1456
0
        }
1457
0
    }
1458
0
    return status;
1459
0
}
1460
1461
1462
OFBool OFStandard::deleteFile(const OFFilename &filename)
1463
0
{
1464
0
    int err = -1;
1465
    /* avoid NULL or empty string passed to unlink() */
1466
0
    if (!filename.isEmpty())
1467
0
    {
1468
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
1469
        if (filename.usesWideChars())
1470
            err = _wunlink(filename.getWideCharPointer());
1471
        else
1472
#endif
1473
0
            err = unlink(filename.getCharPointer());
1474
0
    }
1475
0
    return (err == 0);
1476
0
}
1477
1478
1479
OFBool OFStandard::renameFile(const OFFilename &oldFilename,
1480
                              const OFFilename &newFilename)
1481
0
{
1482
0
    int err = -1;
1483
    /* avoid NULL or empty strings passed to rename() */
1484
0
    if (!oldFilename.isEmpty() && !newFilename.isEmpty())
1485
0
    {
1486
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
1487
        if (oldFilename.usesWideChars() && newFilename.usesWideChars())
1488
            err = _wrename(oldFilename.getWideCharPointer(), newFilename.getWideCharPointer());
1489
        else {
1490
            const char *oldName = oldFilename.getCharPointer();
1491
            const char *newName = newFilename.getCharPointer();
1492
            /* avoid passing invalid values to rename() */
1493
            if ((oldName != NULL) && (newName != NULL))
1494
                err = rename(oldName, newName);
1495
        }
1496
#else
1497
0
        err = rename(oldFilename.getCharPointer(), newFilename.getCharPointer());
1498
0
#endif
1499
0
    }
1500
0
    return (err == 0);
1501
0
}
1502
1503
1504
size_t OFStandard::getFileSize(const OFFilename &filename)
1505
0
{
1506
0
    size_t fileSize = 0;
1507
    /* avoid NULL or empty strings passed to stat() */
1508
0
    if (!filename.isEmpty())
1509
0
    {
1510
#if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
1511
        if (filename.usesWideChars())
1512
        {
1513
            struct _stat64i32 fileStat;
1514
            if (_wstat(filename.getWideCharPointer(), &fileStat) == 0)
1515
                fileSize = OFstatic_cast(size_t, fileStat.st_size);
1516
        } else
1517
#endif
1518
0
        {
1519
0
            struct stat fileStat;
1520
0
            if (stat(filename.getCharPointer(), &fileStat) == 0)
1521
0
                fileSize = OFstatic_cast(size_t, fileStat.st_size);
1522
0
        }
1523
0
    }
1524
0
    return fileSize;
1525
0
}
1526
1527
1528
OFBool OFStandard::checkForMarkupConversion(const OFString &sourceString,
1529
                                            const OFBool convertNonASCII,
1530
                                            const size_t maxLength)
1531
0
{
1532
0
    OFBool result = OFFalse;
1533
0
    size_t pos = 0;
1534
0
    const size_t strLen = sourceString.length();
1535
    /* determine maximum number of characters to be converted */
1536
0
    const size_t length = (maxLength == 0) ? strLen : ((strLen < maxLength) ? strLen : maxLength);
1537
    /* check for characters to be converted */
1538
0
    while (pos < length)
1539
0
    {
1540
0
        const size_t c = OFstatic_cast(unsigned char, sourceString.at(pos));
1541
0
        if ((c == '<') || (c == '>') || (c == '&') || (c == '"') || (c == '\'') ||
1542
0
            (c == 0) || /* a NULL byte should never be added to the output */
1543
0
            (c == 10) || (c == 13) || (convertNonASCII && ((c < 32) || (c >= 127))))
1544
0
        {
1545
            /* return on the first character that needs to be converted */
1546
0
            result = OFTrue;
1547
0
            break;
1548
0
        }
1549
0
        ++pos;
1550
0
    }
1551
0
    return result;
1552
0
}
1553
1554
1555
OFCondition OFStandard::convertToMarkupStream(STD_NAMESPACE ostream &out,
1556
                                              const OFString &sourceString,
1557
                                              const OFBool convertNonASCII,
1558
                                              const E_MarkupMode markupMode,
1559
                                              const OFBool newlineAllowed,
1560
                                              const size_t maxLength)
1561
0
{
1562
0
    size_t pos = 0;
1563
0
    const size_t strLen = sourceString.length();
1564
    /* determine maximum number of characters to be converted */
1565
0
    const size_t length = (maxLength == 0) ? strLen : ((strLen < maxLength) ? strLen : maxLength);
1566
    /* replace HTML/XHTML/XML reserved characters */
1567
0
    while (pos < length)
1568
0
    {
1569
0
        const char c = sourceString.at(pos);
1570
        /* less than */
1571
0
        if (c == '<')
1572
0
            out << "&lt;";
1573
        /* greater than */
1574
0
        else if (c == '>')
1575
0
            out << "&gt;";
1576
        /* ampersand */
1577
0
        else if (c == '&')
1578
0
            out << "&amp;";
1579
        /* quotation mark */
1580
0
        else if (c == '"')
1581
0
        {
1582
            /* entity "&quot;" is not defined in HTML 3.2 */
1583
0
            if (markupMode == MM_HTML32)
1584
0
                out << "&#34;";
1585
0
            else
1586
0
                out << "&quot;";
1587
0
        }
1588
        /* apostrophe */
1589
0
        else if (c == '\'')
1590
0
        {
1591
            /* entity "&apos;" is not defined in HTML */
1592
0
            if ((markupMode == MM_HTML) || (markupMode == MM_HTML32))
1593
0
                out << "&#39;";
1594
0
            else
1595
0
                out << "&apos;";
1596
0
        }
1597
        /* newline: LF, CR, LF CR, CR LF */
1598
0
        else if ((c == '\012') || (c == '\015'))
1599
0
        {
1600
0
            if (markupMode == MM_XML)
1601
0
            {
1602
                /* encode CR and LF exactly as specified */
1603
0
                if (c == '\012')
1604
0
                    out << "&#10;";    // '\n'
1605
0
                else
1606
0
                    out << "&#13;";    // '\r'
1607
0
            } else {  /* HTML/XHTML mode */
1608
                /* skip next character if it belongs to the newline sequence */
1609
0
                if (((c == '\012') && (sourceString[pos + 1] == '\015')) || ((c == '\015') && (sourceString[pos + 1] == '\012')))
1610
0
                    ++pos;
1611
0
                if (newlineAllowed)
1612
0
                {
1613
0
                    if (markupMode == MM_XHTML)
1614
0
                        out << "<br />\n";
1615
0
                    else
1616
0
                        out << "<br>\n";
1617
0
                } else
1618
0
                    out << "&para;";
1619
0
            }
1620
0
        } else {
1621
0
            const size_t charValue = OFstatic_cast(unsigned char, c);
1622
            /* other character: ... */
1623
0
            if ((convertNonASCII || (markupMode == MM_HTML32)) && ((charValue < 32) || (charValue >= 127)))
1624
0
            {
1625
                /* convert < #32 and >= #127 to Unicode (ISO Latin-1) */
1626
0
                out << "&#" << charValue << ";";
1627
0
            }
1628
0
            else if (charValue != 0)
1629
0
            {
1630
                /* just append (if not a NULL byte) */
1631
0
                out << c;
1632
0
            }
1633
0
        }
1634
0
        ++pos;
1635
0
    }
1636
0
    return EC_Normal;
1637
0
}
1638
1639
1640
const OFString &OFStandard::convertToMarkupString(const OFString &sourceString,
1641
                                                  OFString &markupString,
1642
                                                  const OFBool convertNonASCII,
1643
                                                  const E_MarkupMode markupMode,
1644
                                                  const OFBool newlineAllowed,
1645
                                                  const size_t maxLength)
1646
0
{
1647
0
    OFStringStream stream;
1648
    /* call stream variant of convert to markup */
1649
0
    if (OFStandard::convertToMarkupStream(stream, sourceString, convertNonASCII, markupMode, newlineAllowed, maxLength).good())
1650
0
    {
1651
0
        stream << OFStringStream_ends;
1652
        /* convert string stream into a character string */
1653
0
        OFSTRINGSTREAM_GETSTR(stream, buffer_str)
1654
0
        markupString.assign(buffer_str);
1655
0
        OFSTRINGSTREAM_FREESTR(buffer_str)
1656
0
    } else
1657
0
        markupString.clear();
1658
0
    return markupString;
1659
0
}
1660
1661
1662
OFBool OFStandard::checkForOctalConversion(const OFString &sourceString,
1663
                                           const size_t maxLength)
1664
0
{
1665
0
    OFBool result = OFFalse;
1666
0
    size_t pos = 0;
1667
0
    const size_t strLen = sourceString.length();
1668
    /* determine maximum number of characters to be converted */
1669
0
    const size_t length = (maxLength == 0) ? strLen : ((strLen < maxLength) ? strLen : maxLength);
1670
    /* check for characters to be converted */
1671
0
    while (pos < length)
1672
0
    {
1673
0
        const size_t c = OFstatic_cast(unsigned char, sourceString.at(pos));
1674
0
        if ((c < 32) || (c >= 127))
1675
0
        {
1676
            /* return on the first character that needs to be converted */
1677
0
            result = OFTrue;
1678
0
            break;
1679
0
        }
1680
0
        ++pos;
1681
0
    }
1682
0
    return result;
1683
0
}
1684
1685
1686
OFCondition OFStandard::convertToOctalStream(STD_NAMESPACE ostream &out,
1687
                                             const OFString &sourceString,
1688
                                             const size_t maxLength)
1689
0
{
1690
0
    size_t pos = 0;
1691
0
    const size_t strLen = sourceString.length();
1692
    /* determine maximum number of characters to be converted */
1693
0
    const size_t length = (maxLength == 0) ? strLen : ((strLen < maxLength) ? strLen : maxLength);
1694
    /* switch to octal mode for numbers */
1695
0
    out << STD_NAMESPACE oct << STD_NAMESPACE setfill('0');
1696
0
    while (pos < length)
1697
0
    {
1698
0
        const char c = sourceString.at(pos);
1699
0
        const size_t charValue = OFstatic_cast(unsigned char, c);
1700
        /* replace non-ASCII characters */
1701
0
        if ((charValue < 32) || (charValue >= 127))
1702
0
            out << '\\' << STD_NAMESPACE setw(3) << charValue;
1703
0
        else
1704
0
            out << c;
1705
0
        ++pos;
1706
0
    }
1707
    /* reset i/o manipulators */
1708
0
    out << STD_NAMESPACE dec << STD_NAMESPACE setfill(' ');
1709
0
    return EC_Normal;
1710
0
}
1711
1712
1713
const OFString &OFStandard::convertToOctalString(const OFString &sourceString,
1714
                                                 OFString &octalString,
1715
                                                 const size_t maxLength)
1716
0
{
1717
0
    OFStringStream stream;
1718
    /* call stream variant of convert to octal notation */
1719
0
    if (OFStandard::convertToOctalStream(stream, sourceString, maxLength).good())
1720
0
    {
1721
0
        stream << OFStringStream_ends;
1722
        /* convert string stream into a character string */
1723
0
        OFSTRINGSTREAM_GETSTR(stream, buffer_str)
1724
0
        octalString.assign(buffer_str);
1725
0
        OFSTRINGSTREAM_FREESTR(buffer_str)
1726
0
    } else
1727
0
        octalString.clear();
1728
0
    return octalString;
1729
0
}
1730
1731
1732
// Base64 translation table as described in RFC 2045 (MIME)
1733
static const char enc_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1734
1735
OFCondition OFStandard::encodeBase64(STD_NAMESPACE ostream &out,
1736
                                     const unsigned char *data,
1737
                                     const size_t length,
1738
                                     const size_t width)
1739
0
{
1740
0
    OFCondition status = EC_IllegalParameter;
1741
    /* check data buffer to be encoded */
1742
0
    if (data != NULL)
1743
0
    {
1744
0
        unsigned char c;
1745
0
        size_t w = 0;
1746
        /* iterate over all data elements */
1747
0
        for (size_t i = 0; i < length; i++)
1748
0
        {
1749
            /* encode first 6 bits */
1750
0
            out << enc_base64[(data[i] >> 2) & 0x3f];
1751
            /* insert line break (if width > 0) */
1752
0
            if (++w == width)
1753
0
            {
1754
0
                out << OFendl;
1755
0
                w = 0;
1756
0
            }
1757
            /* encode remaining 2 bits of the first byte and 4 bits of the second byte */
1758
0
            c = (data[i] << 4) & 0x3f;
1759
0
            if (++i < length)
1760
0
                c |= (data[i] >> 4) & 0x0f;
1761
0
            out << enc_base64[c];
1762
            /* insert line break (if width > 0) */
1763
0
            if (++w == width)
1764
0
            {
1765
0
                out << OFendl;
1766
0
                w = 0;
1767
0
            }
1768
            /* encode remaining 4 bits of the second byte and 2 bits of the third byte */
1769
0
            if (i < length)
1770
0
            {
1771
0
                c = (data[i] << 2) & 0x3f;
1772
0
                if (++i < length)
1773
0
                    c |= (data[i] >> 6) & 0x03;
1774
0
                out << enc_base64[c];
1775
0
            } else {
1776
0
                i++;
1777
                /* append fill char */
1778
0
                out << '=';
1779
0
            }
1780
            /* insert line break (if width > 0) */
1781
0
            if (++w == width)
1782
0
            {
1783
0
                out << OFendl;
1784
0
                w = 0;
1785
0
            }
1786
            /* encode remaining 6 bits of the third byte */
1787
0
            if (i < length)
1788
0
                out << enc_base64[data[i] & 0x3f];
1789
0
            else /* append fill char */
1790
0
                out << '=';
1791
            /* insert line break (if width > 0) */
1792
0
            if (++w == width)
1793
0
            {
1794
0
                out << OFendl;
1795
0
                w = 0;
1796
0
            }
1797
0
        }
1798
        /* flush stream */
1799
0
        out.flush();
1800
0
        status = EC_Normal;
1801
0
    }
1802
0
    return status;
1803
0
}
1804
1805
1806
const OFString &OFStandard::encodeBase64(const unsigned char *data,
1807
                                         const size_t length,
1808
                                         OFString &result,
1809
                                         const size_t width)
1810
0
{
1811
0
    OFStringStream stream;
1812
    /* call stream variant of Base64 encoder */
1813
0
    if (OFStandard::encodeBase64(stream, data, length, width).good())
1814
0
    {
1815
0
        stream << OFStringStream_ends;
1816
        /* convert string stream into a character string */
1817
0
        OFSTRINGSTREAM_GETSTR(stream, buffer_str)
1818
0
        result.assign(buffer_str);
1819
0
        OFSTRINGSTREAM_FREESTR(buffer_str)
1820
0
    } else
1821
0
        result.clear();
1822
0
    return result;
1823
0
}
1824
1825
1826
// Base64 decoding table: maps #43..#122 to #0..#63 (255 means invalid)
1827
static const unsigned char dec_base64[] =
1828
  { 62, 255, 255, 255, 63,                                                                                  // '+' .. '/'
1829
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61,                                                                 // '0' .. '9'
1830
    255, 255, 255, 255, 255, 255, 255,                                                                      // ':' .. '@'
1831
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,           // 'A' .. 'Z'
1832
    255, 255, 255, 255, 255, 255,                                                                           // '[' .. '`'
1833
    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51  // 'a' .. 'z'
1834
  };
1835
1836
size_t OFStandard::decodeBase64(const OFString &data,
1837
                                unsigned char *&result)
1838
0
{
1839
0
    size_t count = 0;
1840
    /* search for fill char to determine the real length of the input string */
1841
0
    const size_t fillPos = data.find('=');
1842
0
    const size_t length = (fillPos != OFString_npos) ? fillPos : data.length();
1843
    /* check data buffer to be decoded */
1844
0
    if (length > 0)
1845
0
    {
1846
        /* allocate sufficient memory for the decoded data */
1847
0
        result = new unsigned char[((length + 3) / 4) * 3];
1848
0
        if (result != NULL)
1849
0
        {
1850
0
            unsigned char c1 = 0;
1851
0
            unsigned char c2 = 0;
1852
            /* iterate over all data elements */
1853
0
            for (size_t i = 0; i < length; i++)
1854
0
            {
1855
                /* skip invalid characters and assign first decoded char */
1856
0
                while ((i < length) && ((data.at(i) < '+') || (data.at(i) > 'z') || ((c1 = dec_base64[data.at(i) - '+']) > 63)))
1857
0
                    i++;
1858
0
                if (++i < length)
1859
0
                {
1860
                    /* skip invalid characters and assign second decoded char */
1861
0
                    while ((i < length) && ((data.at(i) < '+') || (data.at(i) > 'z') || ((c2 = dec_base64[data.at(i) - '+']) > 63)))
1862
0
                        i++;
1863
0
                    if (i < length)
1864
0
                    {
1865
                        /* decode first byte */
1866
0
                        result[count++] = OFstatic_cast(unsigned char, (c1 << 2) | ((c2 >> 4) & 0x3));
1867
0
                        if (++i < length)
1868
0
                        {
1869
                            /* skip invalid characters and assign third decoded char */
1870
0
                            while ((i < length) && ((data.at(i) < '+') || (data.at(i) > 'z') || ((c1 = dec_base64[data.at(i) - '+']) > 63)))
1871
0
                                i++;
1872
0
                            if (i < length)
1873
0
                            {
1874
                                /* decode second byte */
1875
0
                                result[count++] = OFstatic_cast(unsigned char, ((c2 << 4) & 0xf0) | ((c1 >> 2) & 0xf));
1876
0
                                if (++i < length)
1877
0
                                {
1878
                                    /* skip invalid characters and assign fourth decoded char */
1879
0
                                    while ((i < length) && ((data.at(i) < '+') || (data.at(i) > 'z') || ((c2 = dec_base64[data.at(i) - '+']) > 63)))
1880
0
                                        i++;
1881
                                    /* decode third byte */
1882
0
                                    if (i < length)
1883
0
                                        result[count++] = OFstatic_cast(unsigned char, ((c1 << 6) & 0xc0) | c2);
1884
0
                                }
1885
0
                            }
1886
0
                        }
1887
0
                    }
1888
0
                }
1889
0
            }
1890
            /* delete buffer if no data has been written to the output */
1891
0
            if (count == 0)
1892
0
            {
1893
0
                delete[] result;
1894
0
                result = NULL;
1895
0
            }
1896
0
        }
1897
0
    } else
1898
0
        result = NULL;
1899
0
    return count;
1900
0
}
1901
1902
#ifndef ENABLE_OLD_OFSTD_ATOF_IMPLEMENTATION
1903
1904
double OFStandard::atof(const char *s, OFBool *success)
1905
0
{
1906
0
  double d = 0.0;
1907
0
  if (success) *success = OFFalse;
1908
0
  if (s)
1909
0
  {
1910
    // convert input to a string object
1911
0
    STD_NAMESPACE string ss(s);
1912
1913
    // erase leading whitespace
1914
0
    ss.erase(0, ss.find_first_not_of("\t "));
1915
1916
    // handle NaN as a special case, since iostream does not.
1917
    // sscanf may or may not handle this case internally, depending on the version of the C standard implemented. NaN and inf will be supported in C99.
1918
0
    if ((ss.length() >= 3) && (ss[0] == 'n' || ss[0] == 'N') && (ss[1] == 'a' || ss[1] == 'A') && (ss[2] == 'n' || ss[2] == 'N'))
1919
0
    {
1920
0
        if (success) *success = OFTrue;
1921
0
        return OFnumeric_limits<double>::quiet_NaN();
1922
0
    }
1923
1924
    // handle negative NaN as a special case, since iostream does not
1925
0
    if ((ss.length() >= 4) && (ss[0] == '-') && (ss[1] == 'n' || ss[1] == 'N') && (ss[2] == 'a' || ss[2] == 'A') && (ss[3] == 'n' || ss[3] == 'N'))
1926
0
    {
1927
0
        if (success) *success = OFTrue;
1928
0
        return OFnumeric_limits<double>::quiet_NaN();
1929
0
    }
1930
1931
    // handle positive infinity as a special case, since iostream does not
1932
0
    if ((ss.length() >= 3) && (ss[0] == 'i' || ss[0] == 'I') && (ss[1] == 'n' || ss[1] == 'N') && (ss[2] == 'f' || ss[2] == 'F'))
1933
0
    {
1934
0
        if (success) *success = OFTrue;
1935
0
        return OFnumeric_limits<double>::infinity();
1936
0
    }
1937
1938
    // handle negative infinity as a special case, since iostream does not
1939
0
    if ((ss.length() >= 4) && (ss[0] == '-') && (ss[1] == 'i' || ss[1] == 'I') && (ss[2] == 'n' || ss[2] == 'N') && (ss[3] == 'f' || ss[3] == 'F'))
1940
0
    {
1941
0
        if (success) *success = OFTrue;
1942
0
        return -OFnumeric_limits<double>::infinity();
1943
0
    }
1944
1945
0
#ifdef ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION
1946
1947
    // create an input string stream
1948
0
    STD_NAMESPACE istringstream iss(s);
1949
1950
    // create a locale object for the C locale and activate it in the stream
1951
0
    STD_NAMESPACE locale mylocale("C");
1952
0
    iss.imbue(mylocale);
1953
1954
    // convert string to double and set success flag
1955
0
    if ((iss >> d) && success) *success = OFTrue;
1956
1957
#else /* ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION */
1958
1959
// This is the implementation in use when ENABLE_CSTDIO_BASED_ATOF_IMPLEMENTATION
1960
// is defined.
1961
1962
#ifdef _WIN32
1963
1964
    // Windows has a sscanf version where we can explicitly pass a locale
1965
    _locale_t localeInfo = _create_locale(LC_NUMERIC, "C");
1966
    if (_sscanf_l(ss.c_str(),"%lf",localeInfo,&d) == 1 && success ) *success = OFTrue;
1967
    _free_locale(localeInfo);
1968
1969
#else /* _WIN32 */
1970
1971
    // handle the case that the decimal separator expected by sscanf is not "."
1972
    size_t separator_pos = ss.find('.');
1973
    if (separator_pos != STD_NAMESPACE string::npos)
1974
    {
1975
        struct lconv *loc = localeconv();
1976
        if (loc && loc->decimal_point && (0 != strcmp(".", loc->decimal_point)))
1977
        {
1978
          // current locale is using a different decimal separator.
1979
          // Replace "." by the separator expected by sscanf.
1980
          ss.erase(separator_pos, 1);
1981
          ss.insert(separator_pos, loc->decimal_point);
1982
        }
1983
    }
1984
1985
    // note that there is a race condition here. If another thread calls
1986
    // setlocale() between our calls to localeconv() and sscanf(), then
1987
    // things may go wrong, i.e. the conversion may yield an incorrect result
1988
    // because the separator character expected by sscanf has suddenly changed.
1989
    if (sscanf(ss.c_str(),"%lf",&d) == 1 && success ) *success = OFTrue;
1990
1991
#endif /* _WIN32 */
1992
1993
#endif /* ENABLE_IOSTREAM_BASED_ATOF_IMPLEMENTATION */
1994
1995
0
  }
1996
0
  return d;
1997
0
}
1998
1999
#else
2000
2001
// Old implementation of OFStandard::atof(). This implementation may produce
2002
// rounding errors (see DCMTK issue #1100) and has thus been replaced by
2003
// a new implementation based on std::istringstream.
2004
2005
/* Largest possible base 10 exponent.  Any exponent larger than this will
2006
 * already produce underflow or overflow, so there's no need to worry
2007
 * about additional digits.
2008
 */
2009
#define ATOF_MAXEXPONENT 511
2010
2011
/* Table giving binary powers of 10.  Entry is 10^2^i.
2012
 * Used to convert decimal exponents into floating-point numbers.
2013
 */
2014
static const double atof_powersOf10[] =
2015
{
2016
    10.,
2017
    100.,
2018
    1.0e4,
2019
    1.0e8,
2020
    1.0e16,
2021
    1.0e32,
2022
    1.0e64,
2023
    1.0e128,
2024
    1.0e256
2025
};
2026
2027
double OFStandard::atof(const char *s, OFBool *success)
2028
{
2029
    if (success) *success = OFFalse;
2030
    const char *p = s;
2031
    char c;
2032
    int sign = 0;
2033
    int expSign = 0;
2034
    double fraction;
2035
    int exponent = 0; // Exponent read from "EX" field.
2036
    int old_exponent = 0;
2037
    const char *pExp; // Temporarily holds location of exponent in string.
2038
2039
    /* Exponent that derives from the fractional part.  Under normal
2040
     * circumstances, it is the negative of the number of digits in F.
2041
     * However, if I is very long, the last digits of I get dropped
2042
     * (otherwise a long I with a large negative exponent could cause an
2043
     * unnecessary overflow on I alone).  In this case, fracExp is
2044
     * incremented one for each dropped digit.
2045
     */
2046
    int fracExp = 0;
2047
2048
    // Strip off leading blanks and check for a sign.
2049
    while (OFStandard::isspace(*p)) ++p;
2050
2051
    if (*p == '-')
2052
    {
2053
        sign = 1;
2054
        ++p;
2055
    }
2056
    else
2057
    {
2058
        if (*p == '+') ++p;
2059
    }
2060
2061
    //Check for special cases like NaN
2062
    if ((p[0] == 'n' || p[0] == 'N') && (p[1] == 'a' || p[1] == 'A') && (p[2] == 'n' || p[2] == 'N')) {
2063
        if (success) *success = OFTrue;
2064
        return OFnumeric_limits<double>::quiet_NaN();
2065
    }
2066
2067
    if ((p[0] == 'i' || p[0] == 'I') && (p[1] == 'n' || p[1] == 'N') && (p[2] == 'f' || p[2] == 'F')) {
2068
        if (success) *success = OFTrue;
2069
        return sign ? -OFnumeric_limits<double>::infinity() : OFnumeric_limits<double>::infinity();
2070
    }
2071
    // Count the number of digits in the mantissa (including the decimal
2072
    // point), and also locate the decimal point.
2073
2074
    int decPt = -1; // Number of mantissa digits BEFORE decimal point.
2075
    int mantSize;     // Number of digits in mantissa.
2076
    for (mantSize = 0; ; ++mantSize)
2077
    {
2078
        c = *p;
2079
        if (!isdigit(OFstatic_cast(unsigned char, c)))
2080
        {
2081
            if ((c != '.') || (decPt >= 0)) break;
2082
            decPt = mantSize;
2083
        }
2084
        ++p;
2085
    }
2086
2087
    /*
2088
     * Now suck up the digits in the mantissa.  Use two integers to
2089
     * collect 9 digits each (this is faster than using floating-point).
2090
     * If the mantissa has more than 18 digits, ignore the extras, since
2091
     * they can't affect the value anyway.
2092
     */
2093
2094
    pExp = p;
2095
    p -= mantSize;
2096
    if (decPt < 0)
2097
      decPt = mantSize;
2098
      else mantSize -= 1; // One of the digits was the point
2099
2100
    if (mantSize > 18)
2101
    {
2102
        fracExp = decPt - 18;
2103
        mantSize = 18;
2104
    }
2105
    else
2106
    {
2107
        fracExp = decPt - mantSize;
2108
    }
2109
2110
    if (mantSize == 0)
2111
    {
2112
      // subject sequence does not have expected form.
2113
      // return 0 and leave success flag set to false
2114
      return 0.0;
2115
    }
2116
    else
2117
    {
2118
        int frac1 = 0;
2119
        for ( ; mantSize > 9; mantSize -= 1)
2120
        {
2121
            c = *p;
2122
            ++p;
2123
            if (c == '.')
2124
            {
2125
                c = *p;
2126
                ++p;
2127
            }
2128
            frac1 = 10*frac1 + (c - '0');
2129
        }
2130
        int frac2 = 0;
2131
        for (; mantSize > 0; mantSize -= 1)
2132
        {
2133
            c = *p;
2134
            ++p;
2135
            if (c == '.')
2136
            {
2137
                c = *p;
2138
                ++p;
2139
            }
2140
            frac2 = 10*frac2 + (c - '0');
2141
        }
2142
        fraction = (1.0e9 * frac1) + frac2;
2143
    }
2144
2145
    // Skim off the exponent.
2146
    p = pExp;
2147
    if ((*p == 'E') || (*p == 'e'))
2148
    {
2149
        ++p;
2150
        if (*p == '-')
2151
        {
2152
            expSign = 1;
2153
            ++p;
2154
        }
2155
        else
2156
        {
2157
            if (*p == '+') ++p;
2158
            expSign = 0;
2159
        }
2160
        while (isdigit(OFstatic_cast(unsigned char, *p)))
2161
        {
2162
            old_exponent = exponent;
2163
            exponent = exponent * 10 + (*p - '0');
2164
            ++p;
2165
            if (exponent < old_exponent)
2166
            {
2167
              // overflow of the exponent. We cannot represent this number in an integer
2168
              // and also not in a double, where the exponent must not be larger than 308.
2169
              if (expSign)
2170
              {
2171
                // negative exponent. return 0 and leave success flag set to false
2172
                return 0.0;
2173
              }
2174
              else
2175
              {
2176
                // positive exponent. return plus/minus HUGE_VAL, depending on the sign bit
2177
                if (sign) return -HUGE_VAL; else return HUGE_VAL;
2178
              }
2179
            }
2180
        }
2181
    }
2182
2183
    if (expSign)
2184
       exponent = fracExp - exponent;
2185
       else exponent = fracExp + exponent;
2186
2187
    /*
2188
     * Generate a floating-point number that represents the exponent.
2189
     * Do this by processing the exponent one bit at a time to combine
2190
     * many powers of 2 of 10. Then combine the exponent with the
2191
     * fraction.
2192
     */
2193
2194
    if (exponent < 0)
2195
    {
2196
        expSign = 1;
2197
        exponent = -exponent;
2198
    }
2199
    else expSign = 0;
2200
2201
    if (exponent > ATOF_MAXEXPONENT) exponent = ATOF_MAXEXPONENT;
2202
    double dblExp = 1.0;
2203
    for (const double *d = atof_powersOf10; exponent != 0; exponent >>= 1, ++d)
2204
    {
2205
        if (exponent & 01) dblExp *= *d;
2206
    }
2207
2208
    if (expSign)
2209
      fraction /= dblExp;
2210
      else fraction *= dblExp;
2211
2212
    if (success) *success = OFTrue;
2213
    if (sign) return -fraction;
2214
    return fraction;
2215
}
2216
2217
#endif /* ENABLE_OLD_OFSTD_ATOF_IMPLEMENTATION */
2218
2219
/* binary "and" mask for format flags */
2220
0
#define FTOA_FORMAT_MASK 0x03
2221
/* default precision is 6 digits */
2222
0
#define FTOA_DEFPREC 6
2223
/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
2224
#define FTOA_MAXEXP          308
2225
/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
2226
#define FTOA_MAXFRACT        39
2227
/* internal buffer size for ftoa code */
2228
#define FTOA_BUFSIZE         (FTOA_MAXEXP+FTOA_MAXFRACT+1)
2229
2230
#ifndef ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION
2231
2232
#ifndef ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION
2233
2234
static void ftoa_convert(
2235
  char *dst,
2236
  size_t siz,
2237
  double val,
2238
  unsigned int flags,
2239
  int width,
2240
  int prec)
2241
{
2242
  // this version of the function uses snprintf to format the output string.
2243
2244
  char buf[FTOA_BUFSIZE];
2245
  OFString s("%"); // this will become the format string
2246
  unsigned char fmtch = 'G';
2247
2248
  // check if val is NAN
2249
  if (OFMath::isnan(val))
2250
  {
2251
    OFStandard::strlcpy(dst, "nan", siz);
2252
    return;
2253
  }
2254
2255
  // check if val is infinity
2256
  if (OFMath::isinf(val))
2257
  {
2258
    if (val < 0)
2259
        OFStandard::strlcpy(dst, "-inf", siz);
2260
        else OFStandard::strlcpy(dst, "inf", siz);
2261
    return;
2262
  }
2263
2264
  // determine format character
2265
  if (flags & OFStandard::ftoa_uppercase)
2266
  {
2267
    if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_e) fmtch = 'E';
2268
    else if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_f) fmtch = 'f'; // there is no uppercase for 'f'
2269
    else
2270
    {
2271
      fmtch = 'G';
2272
    }
2273
  }
2274
  else
2275
  {
2276
    if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_e) fmtch = 'e';
2277
    else if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_f) fmtch = 'f';
2278
    else
2279
    {
2280
      fmtch = 'g';
2281
    }
2282
  }
2283
2284
  if (flags & OFStandard::ftoa_alternate) s += "#";
2285
  if (flags & OFStandard::ftoa_leftadj) s += "-";
2286
  if (flags & OFStandard::ftoa_zeropad) s += "0";
2287
  if (width > 0)
2288
  {
2289
    OFStandard::snprintf(buf, sizeof(buf), "%d", width);
2290
    s += buf;
2291
  }
2292
  if (prec >= 0)
2293
  {
2294
    OFStandard::snprintf(buf, sizeof(buf), ".%d", prec);
2295
    s += buf;
2296
  }
2297
  s += fmtch;
2298
2299
#ifdef _WIN32
2300
2301
#ifdef HAVE__SET_OUTPUT_FORMAT
2302
  // The old Microsoft Visual C Runtime (MSVCRT) used in VS 2013 and older
2303
  // prints 3 exponent digits by default.  This call changes this.
2304
  // This function does not exist anymore in the Universal C Runtime
2305
  // used by VS 2015 or newer, but is still used by MinGW64.
2306
  _set_output_format(_TWO_DIGIT_EXPONENT);
2307
#endif
2308
2309
  // Windows has an sprintf version where we can explicitly pass a locale
2310
  _locale_t localeInfo = _create_locale(LC_NUMERIC, "C");
2311
  _snprintf_s_l(dst, siz, _TRUNCATE, s.c_str(), localeInfo, val);
2312
  _free_locale(localeInfo);
2313
2314
#else /* _WIN32 */
2315
2316
  // On other platforms, we use snprintf() and fix the decimal separator afterwards
2317
  OFStandard::snprintf(dst, siz, s.c_str(), val);
2318
2319
  // adjust for the decimal separator of the locale, which may be different from '.'
2320
  // Since the locale may change at any time, we try doing this without actually accessing the current locale info
2321
  char *c = dst;
2322
  size_t c_len = siz; // remaining buffer size
2323
  OFBool replaced = OFFalse;
2324
  while (*c)
2325
  {
2326
    if (*c == 0) return; // end of string
2327
    if (*c == '.') return; // decimal separator is '.', nothing to do
2328
    if (*c == ',') // decimal separator is ','; adjust and return
2329
    {
2330
      *c = '.';
2331
      return;
2332
    }
2333
2334
    // since the string is null terminated, c+1 must exist if *c is not null
2335
    if ((*c == OFstatic_cast(char, 0xd9) && *(c+1) == OFstatic_cast(char, 0xab)) ||
2336
        (*c == OFstatic_cast(char, 0xab) && *(c+1) == OFstatic_cast(char, 0xd9))) // decimal separator is <U066B> in big endian or little endian byte order
2337
    {
2338
      *c = '.'; // replace first byte with '.'
2339
      ++c;
2340
      --c_len;
2341
      // c_len cannot be zero at this point, but we check anyway
2342
      if (c_len > 0) memmove(c, c+1,  c_len - 1); // move rest of the string one byte ahead. memmove works with overlapping memory areas.
2343
      return;
2344
    }
2345
2346
    if ((*c >= '0' && *c <= '9') ||
2347
       (*c == '-') || (*c == '+') ||
2348
       (*c == 'E') || (*c == 'e') || (*c == ' '))
2349
    {
2350
      // not a decimal separator, skip
2351
      ++c;
2352
      --c_len;
2353
    }
2354
    else
2355
    {
2356
      // unknown character. We assume this is a decimal separator.
2357
      // We also assume that it might be a multi-byte UTF-8 sequence. We replace the
2358
      // first unknown character with '.', then continue and delete all other unknown characters.
2359
      if (replaced)
2360
      {
2361
        // this is not the first unknown character. Delete character, then continue.
2362
        if (c_len > 0) memmove(c, c+1,  c_len - 1); // move rest of the string one byte ahead. memmove works with overlapping memory areas.
2363
        // we do not advance c here, continue at this position with the now shorter remaining string
2364
      }
2365
      else
2366
      {
2367
        // this is the first unknown character. Replace with '.'
2368
        *c = '.';
2369
        ++c;
2370
        --c_len;
2371
        replaced = OFTrue;
2372
      }
2373
    }
2374
  }
2375
#endif /* _WIN32 */
2376
}
2377
2378
#else /* ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION */
2379
2380
// This is the implementation in use when ENABLE_CSTDIO_BASED_FTOA_IMPLEMENTATION
2381
// is defined.
2382
2383
static void ftoa_convert(
2384
  char *dst,
2385
  size_t siz,
2386
  double val,
2387
  unsigned int flags,
2388
  int width,
2389
  int prec)
2390
0
{
2391
  // if target string is NULL or zero bytes long, bail out.
2392
0
  if (!dst || !siz) return;
2393
2394
  // check if val is NAN
2395
0
  if (OFMath::isnan(val))
2396
0
  {
2397
0
    OFStandard::strlcpy(dst, "nan", siz);
2398
0
    return;
2399
0
  }
2400
2401
  // check if val is infinity
2402
0
  if (OFMath::isinf(val))
2403
0
  {
2404
0
    if (val < 0)
2405
0
        OFStandard::strlcpy(dst, "-inf", siz);
2406
0
        else OFStandard::strlcpy(dst, "inf", siz);
2407
0
    return;
2408
0
  }
2409
2410
  // create an output string stream
2411
0
  STD_NAMESPACE ostringstream oss;
2412
2413
  // create a locale object for the C locale and activate it in the stream
2414
0
  STD_NAMESPACE locale mylocale("C");
2415
0
  oss.imbue(mylocale);
2416
2417
  // set width
2418
0
  if (width > 0) oss << STD_NAMESPACE setw(width);
2419
2420
  // set adjustment
2421
0
  if (flags & OFStandard::ftoa_leftadj) oss << STD_NAMESPACE left;
2422
2423
  // set precision
2424
0
  if (prec < 0) prec = FTOA_DEFPREC;
2425
0
  oss << STD_NAMESPACE setprecision(prec);
2426
2427
  // set uppercase
2428
0
  if (flags & OFStandard::ftoa_uppercase) oss << STD_NAMESPACE uppercase;
2429
2430
  // set alternate form
2431
0
  if (flags & OFStandard::ftoa_alternate) oss << STD_NAMESPACE showpoint;
2432
2433
  // set zero padding
2434
0
  if (flags & OFStandard::ftoa_zeropad) oss << STD_NAMESPACE setfill('0') << STD_NAMESPACE internal;
2435
2436
  // set scientific vs fixed format
2437
0
  if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_e) oss << STD_NAMESPACE scientific;
2438
0
  else if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_f) oss << STD_NAMESPACE fixed;
2439
2440
  // insert the value into the string stream
2441
0
  oss << val;
2442
2443
  // create a string object and store the stream content
2444
0
  STD_NAMESPACE string os;
2445
0
  os = oss.str();
2446
2447
  // copy string into target buffer
2448
0
  OFStandard::strlcpy(dst, os.c_str(), siz);
2449
2450
0
  return;
2451
0
}
2452
2453
#endif /* ENABLE_IOSTREAM_BASED_FTOA_IMPLEMENTATION */
2454
2455
void OFStandard::ftoa(
2456
  char *dst,
2457
  size_t siz,
2458
  double val,
2459
  unsigned int flags,
2460
  int width,
2461
  int prec)
2462
0
{
2463
  // special handling for g/G format and precision -2
2464
0
  if ((prec == -2) && ((flags & FTOA_FORMAT_MASK) == 0))
2465
0
  {
2466
    // first attempt conversion with precision=16
2467
0
    ftoa_convert(dst, siz, val, flags, width, 16);
2468
2469
    // and check if round-trip is exact
2470
0
    OFBool success = OFFalse;
2471
0
    double d = OFStandard::atof(dst, &success);
2472
0
    if (!success || d != val)
2473
0
    {
2474
      // really need precision 17 (DBL_DECIMAL_DIG)
2475
0
      ftoa_convert(dst, siz, val, flags, width, 17);
2476
0
    }
2477
0
  }
2478
0
  else
2479
0
  {
2480
0
    ftoa_convert(dst, siz, val, flags, width, prec);
2481
0
  }
2482
0
}
2483
2484
#else /* ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION */
2485
2486
#define FTOA_TODIGIT(c)      ((c) - '0')
2487
#define FTOA_TOCHAR(n)       ((n) + '0')
2488
2489
/** internal helper class that maintains a string buffer
2490
 *  to which characters can be written. If the string buffer
2491
 *  gets full, additional characters are discarded.
2492
 *  The string buffer does not guarantee zero termination.
2493
 */
2494
class FTOAStringBuffer
2495
{
2496
public:
2497
  /** constructor
2498
   *  @param theSize desired size of string buffer, in bytes
2499
   */
2500
  FTOAStringBuffer(unsigned long theSize)
2501
  : buf_(NULL)
2502
  , offset_(0)
2503
  , size_(theSize)
2504
  {
2505
    if (size_ > 0) buf_ = new char[size_];
2506
  }
2507
2508
  /// destructor
2509
  ~FTOAStringBuffer()
2510
  {
2511
    delete[] buf_;
2512
  }
2513
2514
  /** add one character to string buffer. Never overwrites
2515
   *  buffer boundary.
2516
   *  @param c character to add
2517
   */
2518
  inline void put(unsigned char c)
2519
  {
2520
    if (buf_ && (offset_ < size_)) buf_[offset_++] = c;
2521
  }
2522
2523
  // return pointer to string buffer
2524
  const char *getBuffer() const
2525
  {
2526
    return buf_;
2527
  }
2528
2529
private:
2530
  /// pointer to string buffer
2531
  char *buf_;
2532
2533
  /// current offset within buffer
2534
  unsigned long offset_;
2535
2536
  /// size of buffer
2537
  unsigned long size_;
2538
2539
  /// private undefined copy constructor
2540
  FTOAStringBuffer(const FTOAStringBuffer &old);
2541
2542
  /// private undefined assignment operator
2543
  FTOAStringBuffer &operator=(const FTOAStringBuffer &obj);
2544
};
2545
2546
2547
/** writes the given format character and exponent to output string p.
2548
 *  @param p pointer to target string
2549
 *  @param exponent exponent to print
2550
 *  @param fmtch format character
2551
 *  @return pointer to next unused character in output string
2552
 */
2553
static char *ftoa_exponent(char *p, int exponent, char fmtch)
2554
{
2555
  char expbuf[FTOA_MAXEXP];
2556
2557
  *p++ = fmtch;
2558
  if (exponent < 0)
2559
  {
2560
    exponent = -exponent;
2561
    *p++ = '-';
2562
  }
2563
  else *p++ = '+';
2564
  char *t = expbuf + FTOA_MAXEXP;
2565
  if (exponent > 9)
2566
  {
2567
    do
2568
    {
2569
      *--t = OFstatic_cast(char, FTOA_TOCHAR(exponent % 10));
2570
    }
2571
    while ((exponent /= 10) > 9);
2572
    *--t = OFstatic_cast(char, FTOA_TOCHAR(exponent));
2573
    for (; t < expbuf + FTOA_MAXEXP; *p++ = *t++) /* nothing */;
2574
  }
2575
  else
2576
  {
2577
    *p++ = '0';
2578
    *p++ = OFstatic_cast(char, FTOA_TOCHAR(exponent));
2579
  }
2580
2581
  return p;
2582
}
2583
2584
2585
/** round given fraction and adjust text string if round up.
2586
 *  @param fract  fraction to round
2587
 *  @param expon  pointer to exponent, may be NULL
2588
 *  @param start  pointer to start of string to round
2589
 *  @param end    pointer to one char after end of string
2590
 *  @param ch     if fract is zero, this character is interpreted as fraction*10 instead
2591
 *  @param signp  pointer to sign character, '-' or 0.
2592
 *  @return adjusted pointer to start of rounded string, may be start or start-1.
2593
 */
2594
static char *ftoa_round(double fract, int *expon, char *start, char *end, char ch, char *signp)
2595
{
2596
  double tmp;
2597
2598
  if (fract) (void) modf(fract * 10, &tmp);
2599
  else tmp = FTOA_TODIGIT(ch);
2600
2601
  if (tmp > 4)
2602
  {
2603
    for (;; --end)
2604
    {
2605
      if (*end == '.') --end;
2606
      if (++*end <= '9') break;
2607
      *end = '0';
2608
      if (end == start)
2609
      {
2610
        if (expon) /* e/E; increment exponent */
2611
        {
2612
          *end = '1';
2613
          ++*expon;
2614
        }
2615
        else /* f; add extra digit */
2616
        {
2617
          *--end = '1';
2618
          --start;
2619
        }
2620
        break;
2621
      }
2622
    }
2623
  }
2624
  /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
2625
  else if (*signp == '-')
2626
  {
2627
    for (;; --end)
2628
    {
2629
      if (*end == '.') --end;
2630
      if (*end != '0') break;
2631
      if (end == start) *signp = 0; // suppress negative 0
2632
    }
2633
  }
2634
2635
  return start;
2636
}
2637
2638
2639
/** convert double value to string, without padding
2640
 *  @param val double value to be formatted
2641
 *  @param prec    precision, adjusted for FTOA_MAXFRACT
2642
 *  @param flags   formatting flags
2643
 *  @param signp   pointer to sign character, '-' or 0.
2644
 *  @param fmtch   format character
2645
 *  @param startp  pointer to start of target buffer
2646
 *  @param endp    pointer to one char after end of target buffer
2647
 *  @return
2648
 */
2649
static int ftoa_convert(double val, int prec, int flags, char *signp, char fmtch, char *startp, char *endp)
2650
{
2651
  char *p;
2652
  double fract;
2653
  int dotrim = 0;
2654
  int expcnt = 0;
2655
  int gformat = 0;
2656
  double integer, tmp;
2657
2658
  fract = modf(val, &integer);
2659
2660
  /* get an extra slot for rounding. */
2661
  char *t = ++startp;
2662
2663
  /*
2664
   * get integer portion of val; put into the end of the buffer; the
2665
   * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
2666
   */
2667
  for (p = endp - 1; integer; ++expcnt)
2668
  {
2669
    tmp = modf(integer / 10, &integer);
2670
    *p-- = OFstatic_cast(char, FTOA_TOCHAR(OFstatic_cast(int, (tmp + .01) * 10)));
2671
  }
2672
2673
  switch(fmtch)
2674
  {
2675
    case 'f':
2676
      /* reverse integer into beginning of buffer */
2677
      if (expcnt)
2678
      {
2679
        for (; ++p < endp; *t++ = *p);
2680
      }
2681
      else *t++ = '0';
2682
2683
      /*
2684
       * if precision required or alternate flag set, add in a
2685
       * decimal point.
2686
       */
2687
      if (prec || flags & OFStandard::ftoa_alternate) *t++ = '.';
2688
2689
      /* if requires more precision and some fraction left */
2690
      if (fract)
2691
      {
2692
        if (prec) do
2693
        {
2694
          fract = modf(fract * 10, &tmp);
2695
          *t++ = OFstatic_cast(char, FTOA_TOCHAR(OFstatic_cast(int, tmp)));
2696
        } while (--prec && fract);
2697
        if (fract)
2698
        {
2699
          startp = ftoa_round(fract, OFstatic_cast(int *, NULL), startp, t - 1, OFstatic_cast(char, 0), signp);
2700
        }
2701
      }
2702
      for (; prec--; *t++ = '0');
2703
      break;
2704
2705
    case 'e':
2706
    case 'E':
2707
eformat:
2708
      if (expcnt)
2709
      {
2710
        *t++ = *++p;
2711
        if (prec || flags&OFStandard::ftoa_alternate)
2712
                *t++ = '.';
2713
        /* if requires more precision and some integer left */
2714
        for (; prec && ++p < endp; --prec)
2715
                *t++ = *p;
2716
        /*
2717
         * if done precision and more of the integer component,
2718
         * round using it; adjust fract so we don't re-round
2719
         * later.
2720
         */
2721
        if (!prec && ++p < endp)
2722
        {
2723
          fract = 0;
2724
          startp = ftoa_round(OFstatic_cast(double, 0), &expcnt, startp, t - 1, *p, signp);
2725
        }
2726
        /* adjust expcnt for digit in front of decimal */
2727
        --expcnt;
2728
      }
2729
      /* until first fractional digit, decrement exponent */
2730
      else if (fract)
2731
      {
2732
        /* adjust expcnt for digit in front of decimal */
2733
        for (expcnt = -1;; --expcnt) {
2734
                fract = modf(fract * 10, &tmp);
2735
                if (tmp)
2736
                        break;
2737
        }
2738
        *t++ = OFstatic_cast(char, FTOA_TOCHAR(OFstatic_cast(int, tmp)));
2739
        if (prec || flags&OFStandard::ftoa_alternate) *t++ = '.';
2740
      }
2741
      else
2742
      {
2743
        *t++ = '0';
2744
        if (prec || flags&OFStandard::ftoa_alternate) *t++ = '.';
2745
      }
2746
2747
      /* if requires more precision and some fraction left */
2748
      if (fract)
2749
      {
2750
        if (prec) do
2751
        {
2752
          fract = modf(fract * 10, &tmp);
2753
          *t++ = OFstatic_cast(char, FTOA_TOCHAR(OFstatic_cast(int, tmp)));
2754
        } while (--prec && fract);
2755
        if (fract)
2756
        {
2757
          startp = ftoa_round(fract, &expcnt, startp, t - 1, OFstatic_cast(char, 0), signp);
2758
        }
2759
      }
2760
2761
      /* if requires more precision */
2762
      for (; prec--; *t++ = '0');
2763
2764
      /* unless alternate flag, trim any g/G format trailing 0's */
2765
      if (gformat && !(flags&OFStandard::ftoa_alternate))
2766
      {
2767
        while (t > startp && *--t == '0') /* nothing */;
2768
        if (*t == '.') --t;
2769
        ++t;
2770
      }
2771
      t = ftoa_exponent(t, expcnt, fmtch);
2772
      break;
2773
2774
    case 'g':
2775
    case 'G':
2776
      /* a precision of 0 is treated as a precision of 1. */
2777
      if (!prec) ++prec;
2778
      /*
2779
       * ``The style used depends on the value converted; style e
2780
       * will be used only if the exponent resulting from the
2781
       * conversion is less than -4 or greater than the precision.''
2782
       *      -- ANSI X3J11
2783
       */
2784
      if (expcnt > prec || (!expcnt && fract && fract < .0001))
2785
      {
2786
        /*
2787
         * g/G format counts "significant digits, not digits of
2788
         * precision; for the e/E format, this just causes an
2789
         * off-by-one problem, i.e. g/G considers the digit
2790
         * before the decimal point significant and e/E doesn't
2791
         * count it as precision.
2792
         */
2793
        --prec;
2794
        fmtch = OFstatic_cast(char, fmtch - 2);             /* G->E, g->e */
2795
        gformat = 1;
2796
        goto eformat;
2797
      }
2798
2799
      /*
2800
       * reverse integer into beginning of buffer,
2801
       * note, decrement precision
2802
       */
2803
      if (expcnt)
2804
      {
2805
        for (; ++p < endp; *t++ = *p, --prec);
2806
      }
2807
      else *t++ = '0';
2808
      /*
2809
       * if precision required or alternate flag set, add in a
2810
       * decimal point.  If no digits yet, add in leading 0.
2811
       */
2812
      if (prec || flags&OFStandard::ftoa_alternate)
2813
      {
2814
        dotrim = 1;
2815
        *t++ = '.';
2816
      }
2817
      else dotrim = 0;
2818
2819
      /* if requires more precision and some fraction left */
2820
      if (fract)
2821
      {
2822
        if (prec)
2823
        {
2824
          do
2825
          {
2826
            fract = modf(fract * 10, &tmp);
2827
            *t++ = OFstatic_cast(char, FTOA_TOCHAR(OFstatic_cast(int, tmp)));
2828
          } while(!tmp);
2829
          while (--prec && fract)
2830
          {
2831
            fract = modf(fract * 10, &tmp);
2832
            *t++ = OFstatic_cast(char, FTOA_TOCHAR(OFstatic_cast(int, tmp)));
2833
          }
2834
        }
2835
        if (fract)
2836
        {
2837
          startp = ftoa_round(fract, OFstatic_cast(int *, NULL), startp, t - 1, OFstatic_cast(char, 0), signp);
2838
        }
2839
      }
2840
      /* alternate format, adds 0's for precision, else trim 0's */
2841
      if (flags&OFStandard::ftoa_alternate) for (; prec--; *t++ = '0') /* nothing */;
2842
      else if (dotrim)
2843
      {
2844
        while (t > startp && *--t == '0') /* nothing */;
2845
        if (*t != '.') ++t;
2846
      }
2847
  } /* end switch */
2848
2849
  return OFstatic_cast(int, t - startp);
2850
}
2851
2852
void OFStandard::ftoa(
2853
  char *dst,
2854
  size_t siz,
2855
  double val,
2856
  unsigned int flags,
2857
  int width,
2858
  int prec)
2859
{
2860
  // if target string is NULL or zero bytes long, bail out.
2861
  if (!dst || !siz) return;
2862
2863
  // check if val is NAN
2864
  if (OFMath::isnan(val))
2865
  {
2866
    OFStandard::strlcpy(dst, "nan", siz);
2867
    return;
2868
  }
2869
2870
  // check if val is infinity
2871
  if (OFMath::isinf(val))
2872
  {
2873
    if (val < 0)
2874
        OFStandard::strlcpy(dst, "-inf", siz);
2875
        else OFStandard::strlcpy(dst, "inf", siz);
2876
    return;
2877
  }
2878
2879
  int fpprec = 0;     /* `extra' floating precision in [eEfgG] */
2880
  char softsign = 0;  /* temporary negative sign for floats */
2881
  char buf[FTOA_BUFSIZE];      /* space for %c, %[diouxX], %[eEfgG] */
2882
  char sign = '\0';   /* sign prefix (' ', '+', '-', or \0) */
2883
  int n;
2884
  unsigned char fmtch = 'G';
2885
  FTOAStringBuffer sb(FTOA_BUFSIZE+1);
2886
2887
  // determine format character
2888
  if (flags & OFStandard::ftoa_uppercase)
2889
  {
2890
    if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_e) fmtch = 'E';
2891
    else if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_f) fmtch = 'f'; // there is no uppercase for 'f'
2892
    else fmtch = 'G';
2893
  }
2894
  else
2895
  {
2896
    if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_e) fmtch = 'e';
2897
    else if ((flags & FTOA_FORMAT_MASK) == OFStandard::ftoa_format_f) fmtch = 'f';
2898
    else fmtch = 'g';
2899
  }
2900
2901
  // don't do unrealistic precision; just pad it with zeroes later,
2902
  // so buffer size stays rational.
2903
  if (prec > FTOA_MAXFRACT)
2904
  {
2905
    if ((fmtch != 'g' && fmtch != 'G') || (flags&OFStandard::ftoa_alternate)) fpprec = prec - FTOA_MAXFRACT;
2906
    prec = FTOA_MAXFRACT;
2907
  }
2908
  else if (prec == -1) prec = FTOA_DEFPREC;
2909
  else if (prec == -2) prec = 17;
2910
2911
  /*
2912
   * softsign avoids negative 0 if val is < 0 and
2913
   * no significant digits will be shown
2914
   */
2915
  if (val < 0)
2916
  {
2917
    softsign = '-';
2918
    val = -val;
2919
  }
2920
  else softsign = 0;
2921
2922
  /*
2923
   * ftoa_convert may have to round up past the "start" of the
2924
   * buffer, i.e. ``intf("%.2f", (double)9.999);'';
2925
   * if the first char isn't \0, it did.
2926
   */
2927
  *buf = 0;
2928
  int size = ftoa_convert(val, prec, flags, &softsign, fmtch, buf, buf + sizeof(buf));
2929
  if (softsign) sign = '-';
2930
  char *t = *buf ? buf : buf + 1;
2931
2932
  /* At this point, `t' points to a string which (if not flags&OFStandard::ftoa_leftadj)
2933
   * should be padded out to `width' places.  If flags&OFStandard::ftoa_zeropad, it should
2934
   * first be prefixed by any sign or other prefix; otherwise, it should be
2935
   * blank padded before the prefix is emitted.  After any left-hand
2936
   * padding, print the string proper, then emit zeroes required by any
2937
   * leftover floating precision; finally, if OFStandard::ftoa_leftadj, pad with blanks.
2938
   *
2939
   * compute actual size, so we know how much to pad
2940
   */
2941
  int fieldsz = size + fpprec;
2942
  if (sign) fieldsz++;
2943
2944
  /* right-adjusting blank padding */
2945
  if ((flags & (OFStandard::ftoa_leftadj|OFStandard::ftoa_zeropad)) == 0 && width)
2946
  {
2947
    for (n = fieldsz; n < width; n++) sb.put(' ');
2948
  }
2949
2950
  /* prefix */
2951
  if (sign) sb.put(sign);
2952
2953
  /* right-adjusting zero padding */
2954
  if ((flags & (OFStandard::ftoa_leftadj|OFStandard::ftoa_zeropad)) == OFStandard::ftoa_zeropad)
2955
          for (n = fieldsz; n < width; n++)
2956
                  sb.put('0');
2957
2958
  /* the string or number proper */
2959
  n = size;
2960
  while (--n >= 0) sb.put(*t++);
2961
2962
  /* trailing f.p. zeroes */
2963
  while (--fpprec >= 0) sb.put('0');
2964
2965
  /* left-adjusting padding (always blank) */
2966
  if (flags & OFStandard::ftoa_leftadj)
2967
          for (n = fieldsz; n < width; n++)
2968
                  sb.put(' ');
2969
2970
  /* zero-terminate string */
2971
  sb.put(0);
2972
2973
  /* copy result from char buffer to output array */
2974
  const char *c = sb.getBuffer();
2975
  if (c) OFStandard::strlcpy(dst, c, siz); else *dst = 0;
2976
}
2977
2978
#endif /* ENABLE_OLD_OFSTD_FTOA_IMPLEMENTATION */
2979
2980
2981
unsigned int OFStandard::my_sleep(unsigned int seconds)
2982
0
{
2983
#ifdef HAVE_WINDOWS_H
2984
  // on Win32 we use the Sleep() system call which expects milliseconds
2985
  Sleep(1000*seconds);
2986
  return 0;
2987
#elif defined(HAVE_SLEEP)
2988
  // just use the original sleep() system call
2989
0
  return sleep(seconds);
2990
#elif defined(HAVE_USLEEP)
2991
  // usleep() expects microseconds
2992
  (void) usleep(OFstatic_cast(unsigned long, seconds)*1000000UL);
2993
  return 0;
2994
#else
2995
  // don't know how to sleep
2996
  return 0;
2997
#endif
2998
0
}
2999
3000
void OFStandard::milliSleep(unsigned int millisecs)
3001
0
{
3002
#ifdef HAVE_WINDOWS_H
3003
  // on Win32 we use the Sleep() system call which expects milliseconds
3004
    Sleep(millisecs);
3005
#elif defined(HAVE_USLEEP)
3006
    // usleep() expects microseconds
3007
0
    (void) usleep(OFstatic_cast(useconds_t, millisecs * 1000UL));
3008
#else
3009
    struct timeval t;
3010
    t.tv_sec = millisecs / 1000;
3011
    t.tv_usec = (millisecs % 1000) * 1000;
3012
    select(0, NULL, NULL, NULL, &t);
3013
#endif
3014
0
}
3015
3016
3017
long OFStandard::getProcessID()
3018
0
{
3019
#ifdef _WIN32
3020
  return _getpid();
3021
#else
3022
0
  return getpid();
3023
0
#endif
3024
0
}
3025
3026
const unsigned int OFrandr_max = 0x7fffffff;
3027
3028
int OFrand_r(unsigned int &seed)
3029
0
{
3030
0
  unsigned long val = OFstatic_cast(unsigned long, seed);
3031
0
  val = val * 1103515245 + 12345;
3032
0
  seed = OFstatic_cast(unsigned int, val %(OFstatic_cast(unsigned long, 0x80000000)));
3033
0
  return OFstatic_cast(int, seed);
3034
0
}
3035
3036
void OFStandard::trimString(const char*& pBegin, const char*& pEnd)
3037
0
{
3038
0
  assert(pBegin <= pEnd);
3039
0
  while(pBegin != pEnd && (*pBegin == ' ' || !*pBegin))
3040
0
    ++pBegin;
3041
0
  while(pBegin != pEnd && (*(pEnd-1) == ' ' || !*(pEnd-1)))
3042
0
    --pEnd;
3043
0
}
3044
3045
void OFStandard::trimString( const char*& str, size_t& size )
3046
0
{
3047
0
    const char* end = str + size;
3048
0
    trimString( str, end );
3049
0
    size = end - str;
3050
0
}
3051
3052
0
#define MAX_NAME 65536
3053
3054
#ifdef HAVE_GETHOSTBYNAME_R
3055
#ifndef HAVE_PROTOTYPE_GETHOSTBYNAME_R
3056
extern "C" {
3057
    int gethostbyname_r(const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop);
3058
}
3059
#endif
3060
#endif
3061
3062
#ifdef HAVE_GETHOSTBYADDR_R
3063
#ifndef HAVE_PROTOTYPE_GETHOSTBYADDR_R
3064
extern "C" {
3065
    int gethostbyaddr_r(const void *addr, socklen_t len, int type, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop);
3066
}
3067
#endif
3068
#endif
3069
3070
OFString OFStandard::getHostnameByAddress(const char* addr, int len, int type)
3071
0
{
3072
0
  OFString result;
3073
3074
  // We have getaddrinfo(). In this case we also presume that we have
3075
  // getnameinfo(), since both functions were introduced together.
3076
  // This is the preferred implementation, being thread-safe and protocol independent.
3077
0
  OFSockAddr sas;
3078
3079
  // a DNS name must be shorter than 256 characters, so this should be enough
3080
0
  char hostname[512];
3081
0
  hostname[0] = '\0';
3082
0
  socklen_t nameinfo_len;
3083
3084
0
  if (type == AF_INET)
3085
0
  {
3086
0
    if (len != sizeof(struct in_addr)) return result; // invalid address length
3087
0
    struct sockaddr_in *sa4 = sas.getSockaddr_in();
3088
0
    sa4->sin_family = AF_INET;
3089
0
    memcpy(&sa4->sin_addr, addr, len);
3090
0
    nameinfo_len = sizeof(struct sockaddr_in);
3091
0
  }
3092
0
  else if (type == AF_INET6)
3093
0
  {
3094
0
    if (len != sizeof(struct in6_addr)) return result; // invalid address length
3095
0
    struct sockaddr_in6 *sa6 = sas.getSockaddr_in6();
3096
0
    sa6->sin6_family = AF_INET6;
3097
0
    memcpy(&sa6->sin6_addr, addr, len);
3098
0
    nameinfo_len = sizeof(struct sockaddr_in6);
3099
0
  }
3100
0
  else return result; // unknown network type, not supported by getnameinfo()
3101
3102
0
  int err = EAI_AGAIN;
3103
0
  int rep = DCMTK_MAX_EAI_AGAIN_REPETITIONS;
3104
0
  struct sockaddr *sa = sas.getSockaddr();
3105
3106
  // perform reverse DNS lookup. Repeat while we receive temporary failures.
3107
0
  while ((EAI_AGAIN == err) && (rep-- > 0)) err = getnameinfo(sa, nameinfo_len, hostname, 512, NULL, 0, 0);
3108
0
  if ((err == 0) && (hostname[0] != '\0')) result = hostname;
3109
3110
0
  return result;
3111
0
}
3112
3113
3114
void OFStandard::getAddressByHostname(const char *name, int protocolFamily, OFSockAddr& result)
3115
0
{
3116
0
  result.clear();
3117
0
  if (NULL == name) return;
3118
3119
0
  struct addrinfo *result_list = NULL;
3120
0
  int err = EAI_AGAIN;
3121
0
  int rep = DCMTK_MAX_EAI_AGAIN_REPETITIONS;
3122
3123
  // filter for the DNS lookup
3124
0
  ::addrinfo hint = {};
3125
0
  hint.ai_family = protocolFamily;
3126
3127
  // perform DNS lookup. Repeat while we receive temporary failures.
3128
0
  while ((EAI_AGAIN == err) && (rep-- > 0)) err = getaddrinfo(name, NULL, &hint, &result_list);
3129
3130
0
  if (0 == err)
3131
0
  {
3132
0
    if (result_list && result_list->ai_addr)
3133
0
    {
3134
      // DNS lookup successfully completed.
3135
0
      struct sockaddr *result_sa = result.getSockaddr();
3136
0
      memcpy(result_sa, result_list->ai_addr, result_list->ai_addrlen);
3137
0
    }
3138
0
    freeaddrinfo(result_list);
3139
0
  }
3140
0
}
3141
3142
3143
3144
#ifdef HAVE_GRP_H
3145
OFStandard::OFGroup OFStandard::getGrNam( const char* name )
3146
0
{
3147
0
#ifdef HAVE_GETGRNAM_R
3148
0
    unsigned size = 32;
3149
0
    char* tmp = new char[size];
3150
0
    group* res = NULL;
3151
0
    group buf;
3152
0
    while( getgrnam_r( name, &buf, tmp, size, &res ) == ERANGE )
3153
0
    {
3154
0
        delete[] tmp;
3155
0
        if( size >= MAX_NAME )
3156
0
            return NULL;
3157
0
        tmp = new char[size*=2];
3158
0
    }
3159
0
    OFGroup g( res );
3160
0
    delete[] tmp;
3161
0
    return g;
3162
#elif defined HAVE_GETGRNAM
3163
    return OFGroup( getgrnam( name ) );
3164
#else
3165
    return OFGroup( NULL );
3166
#endif
3167
0
}
3168
#endif // HAVE_GRP_H
3169
3170
#ifdef HAVE_PWD_H
3171
OFStandard::OFPasswd OFStandard::getPwNam( const char* name )
3172
0
{
3173
0
#ifdef HAVE_GETPWNAM_R
3174
0
    unsigned size = 32;
3175
0
    char* tmp = new char[size];
3176
0
    passwd* res = NULL;
3177
0
    passwd buf;
3178
0
    while( getpwnam_r( name, &buf, tmp, size, &res ) == ERANGE )
3179
0
    {
3180
0
        delete[] tmp;
3181
0
        if( size >= MAX_NAME )
3182
0
            return NULL;
3183
0
        tmp = new char[size*=2];
3184
0
    }
3185
0
    OFPasswd p( res );
3186
0
    delete[] tmp;
3187
0
    return p;
3188
#elif defined HAVE_GETPWNAM
3189
    return OFPasswd( getpwnam( name ) );
3190
#else
3191
    return OFPasswd( NULL );
3192
#endif
3193
0
}
3194
#endif // HAVE_PWD_H
3195
3196
#ifdef HAVE_GRP_H
3197
OFStandard::OFGroup::OFGroup()
3198
0
: gr_name()
3199
0
, gr_passwd()
3200
0
, gr_mem()
3201
, gr_gid()
3202
0
, ok( OFFalse )
3203
0
{
3204
0
}
3205
3206
OFStandard::OFGroup::OFGroup( group* const g )
3207
0
: gr_name()
3208
0
, gr_passwd()
3209
0
, gr_mem()
3210
, gr_gid()
3211
0
, ok( g != NULL )
3212
0
{
3213
0
    if( ok )
3214
0
    {
3215
0
        gr_name   = g->gr_name;
3216
0
        gr_passwd = g->gr_passwd;
3217
0
        gr_gid    = g->gr_gid;
3218
0
        for( char** m = g->gr_mem; *m; ++m )
3219
0
            gr_mem.push_back( *m );
3220
0
    }
3221
0
}
3222
3223
0
OFBool OFStandard::OFGroup::operator!() const { return !ok; }
3224
0
OFStandard::OFGroup::operator OFBool() const { return ok; }
3225
3226
#endif // #ifdef HAVE_GRP_H
3227
3228
#ifdef HAVE_PWD_H
3229
OFStandard::OFPasswd::OFPasswd()
3230
0
: pw_name()
3231
0
, pw_passwd()
3232
0
, pw_gecos()
3233
0
, pw_dir()
3234
0
, pw_shell()
3235
, pw_uid()
3236
, pw_gid()
3237
0
, ok( OFFalse )
3238
0
{
3239
0
}
3240
3241
OFStandard::OFPasswd::OFPasswd( passwd* const p )
3242
0
: pw_name()
3243
0
, pw_passwd()
3244
0
, pw_gecos()
3245
0
, pw_dir()
3246
0
, pw_shell()
3247
, pw_uid()
3248
, pw_gid()
3249
0
, ok( p != NULL )
3250
0
{
3251
0
    if( ok )
3252
0
    {
3253
0
        pw_name   = p->pw_name;
3254
0
        pw_passwd = p->pw_passwd;
3255
0
        pw_uid    = p->pw_uid;
3256
0
        pw_gid    = p->pw_gid;
3257
0
#ifdef HAVE_PASSWD_GECOS
3258
0
        pw_gecos  = p->pw_gecos;
3259
0
#endif
3260
0
        pw_dir    = p->pw_dir;
3261
0
        pw_shell  = p->pw_shell;
3262
0
    }
3263
0
}
3264
3265
0
OFBool OFStandard::OFPasswd::operator!() const { return !ok; }
3266
0
OFStandard::OFPasswd::operator OFBool() const { return ok; }
3267
3268
#endif // HAVE_PWD_H
3269
3270
OFCondition OFStandard::dropPrivileges()
3271
0
{
3272
0
#if defined(HAVE_SETUID) && defined(HAVE_GETUID)
3273
0
  if ((setuid(getuid()) != 0) && (errno != EPERM))
3274
0
  {
3275
    /* setuid returning nonzero means that the setuid() operation has failed.
3276
     * An errno code of EPERM means that the application was never running with root
3277
     * privileges, i.e. was not installed with setuid root, which is safe and harmless.
3278
     * Other error codes (in particular EAGAIN) signal a problem. Most likely the
3279
     * calling user has already reached the maximum number of permitted processes.
3280
     * In this case the application should rather terminate than continue with
3281
     * full root privileges.
3282
     */
3283
0
    return EC_setuidFailed;
3284
0
  }
3285
0
#endif
3286
0
  return EC_Normal;
3287
0
}
3288
3289
3290
#ifndef HAVE_CXX11
3291
DCMTK_OFSTD_EXPORT OFnullptr_t OFnullptr;
3292
DCMTK_OFSTD_EXPORT OFnullopt_t OFnullopt;
3293
#endif
3294
3295
3296
#ifndef HAVE_STL_TUPLE
3297
static const OFignore_t OFignore_value;
3298
DCMTK_OFSTD_EXPORT const OFignore_t& OFignore = OFignore_value;
3299
OFtuple<> OFmake_tuple() { return OFtuple<>(); }
3300
OFtuple<> OFtie() { return OFtuple<>(); }
3301
#endif
3302
3303
3304
OFString OFStandard::getUserName()
3305
0
{
3306
#ifdef _WIN32
3307
    WKSTA_USER_INFO_0 *userinfo;
3308
    if( NetWkstaUserGetInfo( OFnullptr, 0, OFreinterpret_cast( LPBYTE*, &userinfo ) ) != NERR_Success )
3309
        return "<no-user-information-available>";
3310
    // Convert the Unicode full name to ANSI.
3311
    const WCHAR* const name = OFstatic_cast( WCHAR*, userinfo->wkui0_username );
3312
    OFVector<char> buf( wcslen( name ) * 2 );
3313
    WideCharToMultiByte
3314
    (
3315
        CP_ACP,
3316
        0,
3317
        name,
3318
        -1,
3319
        &*buf.begin(),
3320
        OFstatic_cast(int, buf.size()),
3321
        OFnullptr,
3322
        OFnullptr
3323
    );
3324
    return &*buf.begin();
3325
#elif defined(HAVE_GETLOGIN_R)
3326
    // use getlogin_r instead of getlogin
3327
0
    char buf[513];
3328
0
    if( getlogin_r( buf, 512 ) != 0 )
3329
0
        return "<no-utmp-entry>";
3330
0
    buf[512] = 0;
3331
0
    return buf;
3332
#elif defined(HAVE_GETLOGIN)
3333
    // thread unsafe
3334
    if( const char* s = getlogin() )
3335
        return s;
3336
    return "<no-utmp-entry>";
3337
#elif defined(HAVE_CUSERID)
3338
    char buf[L_cuserid];
3339
    return cuserid( buf );
3340
#else
3341
    return "<unknown-user>";
3342
#endif
3343
0
}
3344
3345
OFString OFStandard::getHostName()
3346
0
{
3347
0
#ifdef HAVE_UNAME
3348
0
    struct utsname n;
3349
0
    uname( &n );
3350
0
    return n.nodename;
3351
#else
3352
    char buf[513];
3353
    gethostname( buf, 512 );
3354
    buf[512] = 0;
3355
    return buf;
3356
#endif
3357
0
}
3358
3359
void OFStandard::initializeNetwork()
3360
0
{
3361
#ifdef HAVE_WINSOCK_H
3362
    WSAData winSockData;
3363
    /* we need at least version 1.1 */
3364
    WORD winSockVersionNeeded = MAKEWORD( 1, 1 );
3365
    WSAStartup(winSockVersionNeeded, &winSockData);
3366
#endif
3367
0
}
3368
3369
void OFStandard::shutdownNetwork()
3370
0
{
3371
#ifdef HAVE_WINSOCK_H
3372
    WSACleanup();
3373
#endif
3374
0
}
3375
3376
OFerror_code OFStandard::getLastSystemErrorCode()
3377
0
{
3378
#ifdef _WIN32
3379
    return OFerror_code( GetLastError(), OFsystem_category() );
3380
#else
3381
0
    return OFerror_code( errno, OFsystem_category() );
3382
0
#endif
3383
0
}
3384
3385
OFerror_code OFStandard::getLastNetworkErrorCode()
3386
0
{
3387
#ifdef HAVE_WINSOCK_H
3388
    return OFerror_code( WSAGetLastError(), OFsystem_category() );
3389
#else
3390
0
    return OFerror_code( errno, OFsystem_category() );
3391
0
#endif
3392
0
}
3393
3394
3395
void OFStandard::forceSleep(Uint32 seconds)
3396
0
{
3397
0
    OFTimer timer;
3398
0
    double elapsed = timer.getDiff();
3399
0
    while (elapsed < OFstatic_cast(double, seconds))
3400
0
    {
3401
        // Use ceiling since otherwise we could wait too short
3402
0
        OFStandard::sleep(OFstatic_cast(unsigned int, ceil(seconds - elapsed)));
3403
0
        elapsed = timer.getDiff();
3404
0
    }
3405
0
}
3406
3407
3408
static const char sanitized_filename_charset[] =
3409
{
3410
  ' ', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '-', '.', '_',
3411
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '_', '_', '_', '_', '_',
3412
  '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
3413
  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '_', '_', '_', '_',
3414
  '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
3415
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '_', '_', '_', '_'
3416
};
3417
3418
3419
void OFStandard::sanitizeFilename(OFString& fname)
3420
0
{
3421
0
    const size_t len = fname.length();
3422
0
    char c;
3423
0
    for (size_t i = 0; i < len; ++i)
3424
0
    {
3425
0
        c = fname[i];
3426
        // Note: an embedded NUL (c == 0) must be treated like any other control
3427
        // character and replaced. It must not reach the table lookup, which
3428
        // would index sanitized_filename_charset[0 - 32] (out-of-bounds read).
3429
0
        if (c < 32 || c >= 127) c = '_'; else c = sanitized_filename_charset[c-32];
3430
0
        fname[i] = c;
3431
0
    }
3432
0
}
3433
3434
3435
void OFStandard::sanitizeFilename(char *fname)
3436
0
{
3437
0
    if (fname)
3438
0
    {
3439
0
        char *c = fname;
3440
0
        while (*c)
3441
0
        {
3442
0
            if (*c < 32 || *c >= 127) *c = '_'; else *c = sanitized_filename_charset[*c-32];
3443
0
            ++c;
3444
0
        }
3445
0
    }
3446
0
}
3447
3448
3449
// Allow list used by sanitizeAETitle(). Index is (byte - 32), so the
3450
// table covers the printable ASCII range 0x20..0x7E. Every entry either
3451
// repeats the input byte (kept) or is '_' (replaced). Kept characters:
3452
// space, '-', '.', ':', '@', '_' and ASCII letters/digits. All shell
3453
// metacharacters and path separators map to '_'.
3454
static const char sanitized_aetitle_charset[] =
3455
{
3456
  ' ', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '-', '.', '_',
3457
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '_', '_', '_', '_', '_',
3458
  '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
3459
  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '_', '_', '_', '_',
3460
  '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
3461
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '_', '_', '_', '_'
3462
};
3463
3464
3465
void OFStandard::sanitizeAETitle(OFString& aetitle)
3466
0
{
3467
    // Preserve a surrounding pair of quotation marks (used by callers
3468
    // that substitute the AE title into an already quoted shell argument).
3469
0
    size_t len = aetitle.length();
3470
0
    size_t start = 0;
3471
0
    if (len >= 2 && aetitle[0] == '"' && aetitle[len - 1] == '"')
3472
0
    {
3473
0
        start = 1;
3474
0
        --len;
3475
0
    }
3476
0
    for (size_t i = start; i < len; ++i)
3477
0
    {
3478
0
        unsigned char c = OFstatic_cast(unsigned char, aetitle[i]);
3479
0
        if (c < 32 || c >= 127) aetitle[i] = '_';
3480
0
        else aetitle[i] = sanitized_aetitle_charset[c - 32];
3481
0
    }
3482
0
}
3483
3484
3485
OFString OFStandard::getDefaultSupportDataDir()
3486
0
{
3487
#ifdef HAVE_WINDOWS_H
3488
  char buf[MAX_PATH+1];
3489
  memset(buf, 0, sizeof(buf));
3490
  (void) ExpandEnvironmentStringsA(DEFAULT_SUPPORT_DATA_DIR, buf, sizeof(buf));
3491
  return buf;
3492
#else
3493
0
  return DEFAULT_SUPPORT_DATA_DIR;
3494
0
#endif
3495
0
}
3496
3497
3498
OFString OFStandard::getDefaultConfigurationDir()
3499
0
{
3500
#ifdef HAVE_WINDOWS_H
3501
  char buf[MAX_PATH+1];
3502
  memset(buf, 0, sizeof(buf));
3503
  (void) ExpandEnvironmentStringsA(DEFAULT_CONFIGURATION_DIR, buf, sizeof(buf));
3504
  return buf;
3505
#else
3506
0
  return DEFAULT_CONFIGURATION_DIR;
3507
0
#endif
3508
0
}
3509
3510
3511
bool OFStandard::isspace(char ch)
3512
73.2k
{
3513
    // This matches every whitespace character in the default locale,
3514
    // as documented in https://en.cppreference.com/w/cpp/string/byte/isspace
3515
73.2k
    switch (ch)
3516
73.2k
    {
3517
406
      case ' ':
3518
679
      case '\f':
3519
968
      case '\n':
3520
980
      case '\r':
3521
1.02k
      case '\t':
3522
1.14k
      case '\v':
3523
1.14k
        return true;
3524
72.0k
      default:
3525
72.0k
        return false;
3526
73.2k
    }
3527
73.2k
}
3528
3529
#include DCMTK_DIAGNOSTIC_IGNORE_STRICT_ALIASING_WARNING
3530
3531
// black magic:
3532
// The C++ standard says that std::in_place should not be called as a function,
3533
// but the linker says we still need a function body. Normally, we would mark
3534
// it as [[noreturn]] and be done, but that's not available pre C++11.
3535
// Therefore, we need a return statement to silence 'missing return statement...'
3536
// style warnings. However, OFin_place_tag is a forward declared struct with
3537
// no actual definition, so, we cannot return an actual OFin_place_tag object.
3538
// Instead, we cast some pointer to it although that is actually bullshit, but
3539
// the code will never be executed anyway. Prior versions of this code returned
3540
// a casted nullptr, but some compilers are just too smart and return a warning
3541
// for that, so, now we cast a pointer to OFnullptr into an OFin_place_tag
3542
// instead to silence the warnings.
3543
0
DCMTK_OFSTD_EXPORT OFin_place_tag OFin_place() { return *reinterpret_cast<const OFin_place_tag*>(&OFnullptr); }