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