xref: /reactos/sdk/lib/ucrt/mbstring/mbsncpy_s.inl (revision 04e0dc4a)
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