1// -*- C++ -*-
2//===----------------------- support/win32/support.h ----------------------===//
3//
4//                     The LLVM Compiler Infrastructure
5//
6// This file is dual licensed under the MIT and the University of Illinois Open
7// Source Licenses. See LICENSE.TXT for details.
8//
9//===----------------------------------------------------------------------===//
10
11#include <cstdarg> // va_start, va_end
12#include <cstddef> // size_t
13#include <cstdlib> // malloc
14#include <cstdio>  // vsprintf, vsnprintf
15#include <cstring> // strcpy, wcsncpy
16#include <cwchar>  // mbstate_t
17
18// Some of these functions aren't standard or if they conform, the name does not.
19
20int asprintf(char **sptr, const char *__restrict format, ...)
21{
22    va_list ap;
23    va_start(ap, format);
24    int result;
25    result = vasprintf(sptr, format, ap);
26    va_end(ap);
27    return result;
28}
29
30// Like sprintf, but when return value >= 0 it returns
31// a pointer to a malloc'd string in *sptr.
32// If return >= 0, use free to delete *sptr.
33int vasprintf( char **sptr, const char *__restrict format, va_list ap )
34{
35    *sptr = NULL;
36    // Query the count required.
37    int count = _vsnprintf( NULL, 0, format, ap );
38    if (count < 0)
39        return count;
40    size_t buffer_size = static_cast<size_t>(count) + 1;
41    char* p = static_cast<char*>(malloc(buffer_size));
42    if ( ! p )
43        return -1;
44    // If we haven't used exactly what was required, something is wrong.
45    // Maybe bug in vsnprintf. Report the error and return.
46    if (_vsnprintf(p, buffer_size, format, ap) != count) {
47        free(p);
48        return -1;
49    }
50    // All good. This is returning memory to the caller not freeing it.
51    *sptr = p;
52    return count;
53}
54
55// Returns >= 0: the number of wide characters found in the
56// multi byte sequence src (of src_size_bytes), that fit in the buffer dst
57// (of max_dest_chars elements size). The count returned excludes the
58// null terminator. When dst is NULL, no characters are copied
59// and no "out" parameters are updated.
60// Returns (size_t) -1: an incomplete sequence encountered.
61// Leaves *src pointing the next character to convert or NULL
62// if a null character was converted from *src.
63size_t mbsnrtowcs( wchar_t *__restrict dst, const char **__restrict src,
64                   size_t src_size_bytes, size_t max_dest_chars, mbstate_t *__restrict ps )
65{
66    const size_t terminated_sequence = static_cast<size_t>(0);
67    //const size_t invalid_sequence = static_cast<size_t>(-1);
68    const size_t incomplete_sequence = static_cast< size_t>(-2);
69
70    size_t dest_converted = 0;
71    size_t source_converted = 0;
72    size_t source_remaining = src_size_bytes;
73    size_t result = 0;
74    bool have_result = false;
75
76    while ( source_remaining ) {
77        if ( dst && dest_converted >= max_dest_chars )
78            break;
79        // Converts one multi byte character.
80        // if result > 0, it's the size in bytes of that character.
81        // othewise if result is zero it indicates the null character has been found.
82        // otherwise it's an error and errno may be set.
83        size_t char_size = mbrtowc( dst ? dst + dest_converted : NULL, *src + source_converted, source_remaining, ps );
84        // Don't do anything to change errno from here on.
85        if ( char_size > 0 ) {
86            source_remaining -= char_size;
87            source_converted += char_size;
88            ++dest_converted;
89            continue;
90        }
91        result = char_size;
92        have_result = true;
93        break;
94    }
95    if ( dst ) {
96        if ( have_result && result == terminated_sequence )
97            *src = NULL;
98        else
99            *src += source_converted;
100    }
101    if ( have_result && result != terminated_sequence && result != incomplete_sequence )
102        return static_cast<size_t>(-1);
103
104    return dest_converted;
105}
106
107// Converts max_source_chars from the wide character buffer pointer to by *src,
108// into the multi byte character sequence buffer stored at dst which must be
109// dst_size_bytes bytes in size.
110// Returns >= 0: the number of bytes in the sequence
111// converted from *src, excluding the null terminator.
112// Returns size_t(-1) if an error occurs, also sets errno.
113// If dst is NULL dst_size_bytes is ignored and no bytes are copied to dst
114// and no "out" parameters are updated.
115size_t wcsnrtombs( char *__restrict dst, const wchar_t **__restrict src,
116                   size_t max_source_chars, size_t dst_size_bytes, mbstate_t *__restrict ps )
117{
118    //const size_t invalid_sequence = static_cast<size_t>(-1);
119
120    size_t source_converted = 0;
121    size_t dest_converted = 0;
122    size_t dest_remaining = dst_size_bytes;
123    size_t char_size = 0;
124    const errno_t no_error = ( errno_t) 0;
125    errno_t result = ( errno_t ) 0;
126    bool have_result = false;
127    bool terminator_found = false;
128
129    while ( source_converted != max_source_chars ) {
130        if ( ! dest_remaining )
131            break;
132        wchar_t c = (*src)[source_converted];
133        if ( dst )
134            result = wcrtomb_s( &char_size, dst + dest_converted, dest_remaining, c, ps);
135        else
136            result = wcrtomb_s( &char_size, NULL, 0, c, ps);
137        // If result is zero there is no error and char_size contains the
138        // size of the multi-byte-sequence converted.
139        // Otherwise result indicates an errno type error.
140        if ( result == no_error ) {
141            if ( c == L'\0' ) {
142                terminator_found = true;
143                break;
144            }
145            ++source_converted;
146            if ( dst )
147                dest_remaining -= char_size;
148            dest_converted += char_size;
149            continue;
150        }
151        have_result = true;
152        break;
153    }
154    if ( dst ) {
155        if ( terminator_found )
156            *src = NULL;
157        else
158            *src = *src + source_converted;
159    }
160    if ( have_result && result != no_error ) {
161        errno = result;
162        return static_cast<size_t>(-1);
163    }
164
165    return dest_converted;
166}
167