1/*** 2*mbsncpy_s.inl - general implementation of _mbsncpy_s and _mbsnbcpy_s 3* 4* Copyright (c) Microsoft Corporation. All rights reserved. 5* 6*Purpose: 7* This file contains the general algorithm for _mbsncpy_s and _mbsnbcpy_s. 8* 9* _COUNT_IN_BYTES defined to 1 implements _mbsnbcpy_s 10* _COUNT_IN_BYTES defined to 0 implements _mbsncpy_s 11* 12****/ 13 14errno_t __cdecl _FUNC_NAME(unsigned char *_Dst, size_t _SizeInBytes, const unsigned char *_Src, size_t _COUNT, _LOCALE_ARG_DECL) 15{ 16 unsigned char *p; 17 size_t available; 18 BOOL fFoundInvalidMBC; 19 BOOL fIsLeadPrefix; 20 21 fFoundInvalidMBC = FALSE; 22 23 if (_COUNT == 0 && _Dst == nullptr && _SizeInBytes == 0) 24 { 25 /* this case is allowed; nothing to do */ 26 _RETURN_NO_ERROR; 27 } 28 29 /* validation section */ 30 _VALIDATE_STRING(_Dst, _SizeInBytes); 31 if (_COUNT == 0) 32 { 33 /* notice that the source string pointer can be NULL in this case */ 34#pragma prefast(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY) /* 26015 */ 35 _RESET_STRING(_Dst, _SizeInBytes); 36 _RETURN_NO_ERROR; 37 } 38 _VALIDATE_POINTER_RESET_STRING(_Src, _Dst, _SizeInBytes); 39 40 _LOCALE_UPDATE; 41 if (_LOCALE_SHORTCUT_TEST) 42 { 43 return strncpy_s((char *)_Dst, _SizeInBytes, (const char *)_Src, _COUNT); 44 } 45 46 p = _Dst; 47 available = _SizeInBytes; 48 if (_COUNT == _TRUNCATE) 49 { 50 while ((*p++ = *_Src++) != 0 && --available > 0) 51 { 52 } 53 54 /* 55 * loop terminates with either: 56 * - src, p pointing 1 byte past null, avail includes the null 57 * - available == 0, p points 1 past end of dst buffer 58 */ 59 } 60 else 61 { 62#if _COUNT_IN_BYTES 63 while ((*p++ = *_Src++) != 0 && --available > 0 && --_COUNT > 0) 64 { 65 } 66 67 /* 68 * loop terminates with either: 69 * - p points 1 byte past null, avail includes null, count includes null 70 * - available == 0, p points 1 past end of dst buffer (inaccessible) 71 * - count == 0, p points 1 past last written byte, space available in dst buffer 72 * 73 * always p[-1] is written. 74 * sometimes p[-1] is null. 75 */ 76#else /* _COUNT_IN_BYTES */ 77 78 /* at this point, avail count be 1. */ 79 80 /* Need to track lead-byte context in order to track character count. */ 81 do 82 { 83 if (_ISMBBLEAD(*_Src)) 84 { 85 if (_Src[1] == 0) 86 { 87 /* 88 * Invalid MBC, write null to dst string, we are finished 89 * copying. We know that available is >= 1, so there is 90 * room for the null termination. If we decrement available 91 * then we will incorrectly report BUFFER_TOO_SMALL. 92 */ 93 94 *p++ = 0; 95 fFoundInvalidMBC = TRUE; 96 break; 97 } 98 if (available <= 2) 99 { 100 /* not enough space for a dbc and null */ 101 available = 0; 102 break; 103 } 104 *p++ = *_Src++; 105 *p++ = *_Src++; 106 available -= 2; 107 } 108 else 109 { 110 if ((*p++ = *_Src++) == 0 || --available == 0) 111 { 112 break; 113 } 114 } 115 } 116 while (--_COUNT > 0); 117#endif /* _COUNT_IN_BYTES */ 118 119 /* If count == 0 then at least one byte was copied and available is still > 0 */ 120 if (_COUNT == 0) 121 { 122 *p++ = 0; 123 /* Note that available is not decremented here. */ 124 } 125 } 126 127 if (available == 0) 128 { 129#if _COUNT_IN_BYTES 130 /* 131 * For COUNT_IN_BYTES, the above loop copied at least one byte so src,p point 132 * past a written byte. 133 */ 134 135 if (*_Src == 0 || _COUNT == 1) 136 { 137 fIsLeadPrefix = FALSE; 138 _ISMBBLEADPREFIX(fIsLeadPrefix, _Dst, &p[-1]); 139 if (fIsLeadPrefix) 140 { 141 /* the source string ended with a lead byte: we remove it */ 142 p[-1] = 0; 143 _RETURN_MBCS_ERROR; 144 } 145 } 146#endif /* _COUNT_IN_BYTES */ 147 148 if (_COUNT == _TRUNCATE) 149 { 150 if (fFoundInvalidMBC) 151 { 152 _SET_MBCS_ERROR; 153 } 154 155 if (_SizeInBytes > 1) 156 { 157 fIsLeadPrefix = FALSE; 158 /* Check if 2nd to last copied byte acted as a lead. 159 * Do not set mbcs error because we are truncating. 160 */ 161 _ISMBBLEADPREFIX(fIsLeadPrefix,_Dst,&_Dst[_SizeInBytes - 2]); 162 if (fIsLeadPrefix) 163 { 164 _Dst[_SizeInBytes - 2] = 0; 165 _FILL_BYTE(_Dst[_SizeInBytes - 1]); 166 _RETURN_TRUNCATE; 167 } 168 } 169 170 _Dst[_SizeInBytes - 1] = 0; 171 _RETURN_TRUNCATE; 172 } 173 _RESET_STRING(_Dst, _SizeInBytes); 174 _RETURN_BUFFER_TOO_SMALL(_Dst, _SizeInBytes); 175 } 176 177#if _COUNT_IN_BYTES 178 /* 179 * COUNT_IN_BYTES copy loop doesn't track lead-byte context, so can't detect 180 * invalid mbc. Detect them here. 181 182 * available < _SizeInBytes means that at least one byte was copied so p is >= &dstBuffer[1] 183 */ 184 185 if ((p - _Dst) >= 2) 186 { 187 _ISMBBLEADPREFIX(fIsLeadPrefix, _Dst,&p[-2]); 188 if (fIsLeadPrefix) 189 { 190 /* the source string ended with a lead byte: we remove it */ 191 p[-2] = 0; 192 available++; 193 fFoundInvalidMBC = TRUE; 194 } 195 } 196#endif /* _COUNT_IN_BYTES */ 197 198 _FILL_STRING(_Dst, _SizeInBytes, _SizeInBytes - available + 1); 199 200 if (fFoundInvalidMBC) 201 { 202 _RETURN_MBCS_ERROR; 203 } 204 205#pragma warning(suppress:__WARNING_POSTCONDITION_NULLTERMINATION_VIOLATION) /* 26036 REVIEW TODO test _mbsnbcpy_s_l */ 206 _RETURN_NO_ERROR; 207} 208 209