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 << "<";
1499 /* greater than */
1500 else if (c == '>')
1501 out << ">";
1502 /* ampersand */
1503 else if (c == '&')
1504 out << "&";
1505 /* quotation mark */
1506 else if (c == '"')
1507 {
1508 /* entity """ is not defined in HTML 3.2 */
1509 if (markupMode == MM_HTML32)
1510 out << """;
1511 else
1512 out << """;
1513 }
1514 /* apostrophe */
1515 else if (c == '\'')
1516 {
1517 /* entity "'" is not defined in HTML */
1518 if ((markupMode == MM_HTML) || (markupMode == MM_HTML32))
1519 out << "'";
1520 else
1521 out << "'";
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 << " "; // '\n'
1531 else
1532 out << " "; // '\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 << "¶";
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