xref: /reactos/sdk/lib/ucrt/mbstring/mbsncat_s.inl (revision 04e0dc4a)
1/***
2*mbsncat_s.inl - general implementation of _mbsncat_s and _mbsnbcat_s
3*
4*       Copyright (c) Microsoft Corporation. All rights reserved.
5*
6*Purpose:
7*       This file contains the general algorithm for _mbsncat_s and _mbsnbcat_s.
8*
9****/
10
11errno_t __cdecl _FUNC_NAME(unsigned char *_Dst, size_t _SizeInBytes, const unsigned char *_Src, size_t _COUNT, _LOCALE_ARG_DECL)
12{
13    unsigned char *p;
14    size_t available;
15    BOOL fIsLeadPrefix;
16    BOOL fFoundInvalidMBC;
17
18    fFoundInvalidMBC = FALSE;
19
20    if (_COUNT == 0 && _Dst == nullptr && _SizeInBytes == 0)
21    {
22        /* this case is allowed; nothing to do */
23        _RETURN_NO_ERROR;
24    }
25
26    /* validation section */
27    _VALIDATE_STRING(_Dst, _SizeInBytes);
28    if (_COUNT != 0)
29    {
30        _VALIDATE_POINTER_RESET_STRING(_Src, _Dst, _SizeInBytes);
31    }
32
33    _LOCALE_UPDATE;
34    if (_LOCALE_SHORTCUT_TEST)
35    {
36        return strncat_s((char *)_Dst, _SizeInBytes, (const char *)_Src, _COUNT);
37    }
38
39    p = _Dst;
40    available = _SizeInBytes;
41    while (available > 0 && *p != 0)
42    {
43        p++;
44        available--;
45    }
46
47    /*
48     * Ran out of room while looking for end of dst string.
49     * p points 1 past end of buffer. We can't look past
50     * end of buffer so can't tell if dst ended with an
51     * invalid mbc.
52     */
53
54    if (available == 0)
55    {
56        _RESET_STRING(_Dst, _SizeInBytes);
57        _RETURN_DEST_NOT_NULL_TERMINATED(_Dst, _SizeInBytes);
58    }
59
60
61    if (available < _SizeInBytes)
62    {
63        /*
64         * Dst may have terminated with an invalid MBCS, in that case we clear
65         * the bogus lead byte.
66         */
67        fIsLeadPrefix = FALSE;
68        _ISMBBLEADPREFIX(fIsLeadPrefix, _Dst, &p[-1]);
69        if (fIsLeadPrefix) {
70            /* the original string ended with a lead byte: we remove it */
71            p--;
72            *p = 0;
73            available++;
74            fFoundInvalidMBC = TRUE;
75        }
76    }
77
78    if (_COUNT == _TRUNCATE)
79    {
80        while ((*p++ = *_Src++) != 0 && --available > 0)
81        {
82        }
83    }
84    else
85    {
86#if _COUNT_IN_BYTES
87        while (_COUNT > 0 && (*p++ = *_Src++) != 0 && --available > 0)
88        {
89            _COUNT--;
90        }
91#else  /* _COUNT_IN_BYTES */
92        while (_COUNT > 0)
93        {
94            if (_ISMBBLEAD(*_Src))
95            {
96                if (_Src[1] == 0)
97                {
98                    /* the source string ended with a lead byte: we remove it */
99                    *p = 0;
100                    fFoundInvalidMBC = TRUE;
101                    break;
102                }
103                if (available <= 2)
104                {
105                    /* not enough space */
106                    available = 0;
107                    break;
108                }
109                *p++ = *_Src++;
110                *p++ = *_Src++;
111                available -= 2;
112            }
113            else
114            {
115                if ((*p++ = *_Src++) == 0 || --available == 0)
116                {
117                    break;
118                }
119            }
120            _COUNT--;
121        }
122#endif  /* _COUNT_IN_BYTES */
123        if (_COUNT == 0)
124        {
125            *p++ = 0;
126        }
127    }
128
129    if (available == 0)
130    {
131#if _COUNT_IN_BYTES
132        /*
133         * defined(_COUNT_IN_BYTES) loop does not track mbc context,
134         * so we must iterate backwards to discover character context.
135         */
136        if (*_Src == 0 || _COUNT == 1)
137        {
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        /*
149         * _COUNT == _TRUNCATE loop terminated because available became 0.
150         * This means that we copied at least one character, and it wasn't
151         * a null. If this last character acted as a lead then overwrite
152         * it with null. Do not set the mbcs error in this case, due that the
153         * user cannot predict this case and he/she's only asking for truncation.
154         */
155        if (_COUNT == _TRUNCATE)
156        {
157            if (fFoundInvalidMBC)
158            {
159                _SET_MBCS_ERROR;
160            }
161
162            if (_SizeInBytes > 1)
163            {
164                fIsLeadPrefix = FALSE;
165                _ISMBBLEADPREFIX(fIsLeadPrefix, _Dst, &_Dst[_SizeInBytes - 2]);
166                if (fIsLeadPrefix)
167                {
168                    _Dst[_SizeInBytes - 2] = 0;
169                    _FILL_BYTE(_Dst[_SizeInBytes - 1]);
170                    _RETURN_TRUNCATE;
171                }
172            }
173
174            _Dst[_SizeInBytes - 1] = 0;
175
176            _RETURN_TRUNCATE;
177        }
178        _RESET_STRING(_Dst, _SizeInBytes);
179        _RETURN_BUFFER_TOO_SMALL(_Dst, _SizeInBytes);
180    }
181#if _COUNT_IN_BYTES
182    if (available < _SizeInBytes)
183    {
184        _ISMBBLEADPREFIX(fIsLeadPrefix, _Dst, &p[-2]);
185        if (fIsLeadPrefix)
186        {
187            /* the source string ended with a lead byte: we remove it */
188            p[-2] = 0;
189            available++;
190            fFoundInvalidMBC = TRUE;
191        }
192    }
193#endif  /* _COUNT_IN_BYTES */
194    _FILL_STRING(_Dst, _SizeInBytes, _SizeInBytes - available + 1);
195
196    if (fFoundInvalidMBC)
197    {
198        _RETURN_MBCS_ERROR;
199    }
200
201    _RETURN_NO_ERROR;
202}
203
204