srt_compat.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /*
  2. * SRT - Secure, Reliable, Transport
  3. * Copyright (c) 2018 Haivision Systems Inc.
  4. *
  5. * This Source Code Form is subject to the terms of the Mozilla Public
  6. * License, v. 2.0. If a copy of the MPL was not distributed with this
  7. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  8. *
  9. */
  10. // Implementation file for srt_compat.h
  11. /*****************************************************************************
  12. written by
  13. Haivision Systems Inc.
  14. *****************************************************************************/
  15. // Prevents from misconfiguration through preprocessor.
  16. #include "platform_sys.h"
  17. #include <srt_compat.h>
  18. #include <string.h>
  19. #include <stdio.h>
  20. #include <errno.h>
  21. #if defined(__unix__) && !defined(BSD) && !defined(SUNOS)
  22. #include <features.h>
  23. #endif
  24. #if defined(_WIN32)
  25. #define WIN32_LEAN_AND_MEAN
  26. #include <windows.h>
  27. #endif
  28. static const char* SysStrError_Fallback(int errnum, char* buf, size_t buflen)
  29. {
  30. #if defined(_MSC_VER) && _MSC_VER < 1900
  31. _snprintf(buf, buflen - 1, "ERROR CODE %d", errnum);
  32. buf[buflen - 1] = '\0';
  33. #else
  34. snprintf(buf, buflen, "ERROR CODE %d", errnum);
  35. #endif
  36. return buf;
  37. }
  38. // This function is a portable and thread-safe version of `strerror`.
  39. // It requires a user-supplied buffer to store the message. The returned
  40. // value is always equal to the given buffer pointer. If the system
  41. // error message is longer than the given buflen, it will be trimmed.
  42. // When the error code is incorrect for the given error message function,
  43. // a fallback message will be returned, either as returned by the underlying
  44. // function, or crafted by this function as a response to error in an
  45. // underlying function.
  46. extern const char * SysStrError(int errnum, char * buf, size_t buflen)
  47. {
  48. if (buf == NULL || buflen < 4) // Required to put ??? into it as a fallback
  49. {
  50. errno = EFAULT;
  51. return buf;
  52. }
  53. buf[0] = '\0';
  54. #if defined(_WIN32)
  55. const char* lpMsgBuf;
  56. // Note: Intentionally the "fixed char size" types are used despite using
  57. // character size dependent FormatMessage (instead of FormatMessageA) so that
  58. // your compilation fails when you use wide characters.
  59. // The problem is that when TCHAR != char, then the buffer written this way
  60. // would have to be converted to ASCII, not just copied by strncpy.
  61. FormatMessageA(0
  62. | FORMAT_MESSAGE_ALLOCATE_BUFFER
  63. | FORMAT_MESSAGE_FROM_SYSTEM
  64. | FORMAT_MESSAGE_IGNORE_INSERTS,
  65. NULL, // no lpSource
  66. errnum, // dwMessageId (as controlled by FORMAT_MESSAGE_FROM_SYSTEM)
  67. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  68. // This below parameter normally should contain a pointer to an allocated buffer,
  69. // and this way it's LPTSTR. But when FORMAT_MESSAGE_ALLOCATE_BUFFER, then it is
  70. // expected to be a the value of LPTSTR* type, converted to LPTSTR, that designates
  71. // a pointer to a variable of type LPTSTR, to which the newly allocated buffer is
  72. // assigned. This buffer should be freed afterwards using LocalFree().
  73. (LPSTR)&lpMsgBuf,
  74. 0, NULL);
  75. if (lpMsgBuf)
  76. {
  77. #ifdef _MSC_VER
  78. strncpy_s(buf, buflen, lpMsgBuf, _TRUNCATE);
  79. #else
  80. strncpy(buf, lpMsgBuf, buflen-1);
  81. buf[buflen-1] = 0;
  82. #endif
  83. LocalFree((HLOCAL)lpMsgBuf);
  84. }
  85. else
  86. {
  87. SysStrError_Fallback(errnum, buf, buflen);
  88. }
  89. return buf;
  90. #elif (!defined(__GNU_LIBRARY__) && !defined(__GLIBC__) ) \
  91. || (( (_POSIX_C_SOURCE >= 200112L) || (_XOPEN_SOURCE >= 600)) && ! _GNU_SOURCE )
  92. // POSIX/XSI-compliant version.
  93. // Overall general POSIX version: returns status.
  94. // 0 for success, otherwise it's:
  95. // - possibly -1 and the error code is in ::errno
  96. // - possibly the error code itself
  97. // The details of the errror are not interesting; simply
  98. // craft a fallback message in this case.
  99. if (strerror_r(errnum, buf, buflen) != 0)
  100. {
  101. return SysStrError_Fallback(errnum, buf, buflen);
  102. }
  103. return buf;
  104. #else
  105. // GLIBC is non-standard under these conditions.
  106. // GNU version: returns the pointer to the message.
  107. // This is either equal to the local buffer (buf)
  108. // or some system-wide (constant) storage. To maintain
  109. // stability of the API, this overall function shall
  110. // always return the local buffer and the message in
  111. // this buffer - so these cases should be distinguished
  112. // and the internal storage copied to the buffer.
  113. char * gnu_buffer = strerror_r(errnum, buf, buflen);
  114. if (!gnu_buffer)
  115. {
  116. // This should never happen, so just a paranoid check
  117. return SysStrError_Fallback(errnum, buf, buflen);
  118. }
  119. // If they are the same, the message is already copied
  120. // (and it's usually a "fallback message" for an error case).
  121. if (gnu_buffer != buf)
  122. {
  123. strncpy(buf, gnu_buffer, buflen-1);
  124. buf[buflen-1] = 0; // guarantee what strncpy doesn't
  125. }
  126. return buf;
  127. #endif
  128. }