1/*
2    Title:  polystring.cpp - String functions and types
3
4    Copyright (c) 2006, 2015 David C.J. Matthews
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License version 2.1 as published by the Free Software Foundation.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#elif defined(_WIN32)
24#include "winconfig.h"
25#else
26#error "No configuration file"
27#endif
28
29#ifdef HAVE_STDIO_H
30#include <stdio.h>
31#endif
32
33#ifdef HAVE_MALLOC_H
34#include <malloc.h>
35#endif
36
37#ifdef HAVE_STDLIB_H
38#include <stdlib.h>
39#endif
40
41#ifdef HAVE_STRING_H
42#include <string.h>
43#endif
44
45#include "globals.h"
46#include "polystring.h"
47#include "run_time.h"
48#include "mpoly.h"
49#include "sys.h"
50#include "arb.h"
51#include "save_vec.h"
52#include "processes.h"
53
54#define SAVE(x) mdTaskData->saveVec.push(x)
55#define SIZEOF(x) (sizeof(x)/sizeof(PolyWord))
56#define DEREFSTRINGHANDLE(_x)    ((PolyStringObject *)(_x)->WordP())
57
58// Return empty string.
59PolyWord EmptyString(TaskData *mdTaskData)
60{
61    // It might be preferable to have a single empty string.
62    PolyStringObject *result = (PolyStringObject *)(alloc(mdTaskData, 1, F_BYTE_OBJ));
63    result->length = 0;
64    return result;
65}
66
67PolyWord C_string_to_Poly(TaskData *mdTaskData, const char *buffer, size_t buffLen)
68/* Returns a C string as a Poly string. */
69{
70    if (buffer == NULL) return EmptyString(mdTaskData);
71
72    if (buffLen == (size_t)-1) buffLen = strlen(buffer);
73
74    /* Get the number of words required, plus 1 for length word,
75       plus flag bit. */
76    PolyStringObject *result = (PolyStringObject *)(alloc(mdTaskData, WORDS(buffLen) + 1, F_BYTE_OBJ));
77
78    /* Set length of string, then copy the characters. */
79    result->length = buffLen;
80    memcpy(result->chars,buffer,buffLen);
81    return result;
82} /* C_string_to_Poly */
83
84POLYUNSIGNED Poly_string_to_C(PolyWord ps, char *buff, POLYUNSIGNED bufflen)
85/* Copies the characters from the string into the destination buffer.
86   Returns original length of string. */
87{
88    PolyStringObject *str = (PolyStringObject *)ps.AsObjPtr();
89    POLYUNSIGNED chars = str->length >= bufflen ? bufflen-1 : str->length;
90    if (chars != 0) strncpy(buff, str->chars, chars);
91    buff[chars] = '\0';
92    return chars;
93} /* Poly_string_to_C */
94
95char *Poly_string_to_C_alloc(PolyWord ps, size_t extraChars)
96/* Similar to Poly_string_to_C except that the string is allocated using
97   malloc and must be freed by the caller. */
98{
99    PolyStringObject * str = (PolyStringObject *)ps.AsObjPtr();
100    POLYUNSIGNED chars = str->length;
101    char    *res = (char*)malloc(chars + extraChars + 1);
102    if (res == 0) return 0;
103    if (chars != 0) strncpy(res, str->chars, chars);
104    res[chars] = '\0';
105    return res;
106} /* Poly_string_to_C_alloc */
107
108TempCString::~TempCString()
109{
110    free(m_value);
111}
112
113#if (defined(_WIN32) && defined(UNICODE))
114
115unsigned int codePage = CP_ACP;
116
117bool setWindowsCodePage(const TCHAR *codePageArg)
118{
119    if (_tcscmp(codePageArg, _T("CP_ACP")) == 0) { codePage = CP_ACP; return true; }
120    if (_tcscmp(codePageArg, _T("CP_UTF7")) == 0 || lstrcmpi(codePageArg, _T("utf7")) == 0)
121         { codePage = CP_UTF7; return true; }
122    if (_tcscmp(codePageArg, _T("CP_UTF8")) == 0 || lstrcmpi(codePageArg, _T("utf8")) == 0)
123         { codePage = CP_UTF8; return true; }
124    if (*codePageArg >= '0' && *codePageArg <= '9')
125    {
126        TCHAR *endp;
127        codePage = _tcstol(codePageArg, &endp, 10);
128        return true;
129    }
130    return false;
131}
132
133/* We need Unicode versions of these. */
134PolyWord C_string_to_Poly(TaskData *mdTaskData, const WCHAR *buffer, size_t buffLen)
135/* Returns a Unicode string as a Poly string. */
136{
137    if (buffer == NULL) return EmptyString(mdTaskData);
138
139    // Get the length of the string, without the terminating null.
140    if (buffLen == -1) buffLen = wcslen(buffer);
141    if (buffLen == 0) return EmptyString(mdTaskData); // If it's zero return empty string.
142
143    // Find the length when converted.
144    int outputLen = WideCharToMultiByte(codePage, 0, buffer, (int)buffLen, NULL, 0, NULL, NULL);
145
146    // Return the null string if there's an error
147    if (outputLen <= 0) return EmptyString(mdTaskData);
148
149    // Get the number of words required, plus 1 for length word, plus flag bit.
150    PolyStringObject *result = (PolyStringObject *)(alloc(mdTaskData, WORDS(outputLen) + 1, F_BYTE_OBJ));
151
152    // Set length of string, then copy the characters.
153    result->length = outputLen;
154    int check = WideCharToMultiByte(codePage, 0, buffer, (int)buffLen, result->chars, outputLen, NULL, NULL);
155    if (check <= 0) return EmptyString(mdTaskData);
156
157    return result;
158}
159
160POLYUNSIGNED Poly_string_to_C(PolyWord ps, WCHAR *buff, POLYUNSIGNED bufflen)
161{
162    PolyStringObject *str = (PolyStringObject *)ps.AsObjPtr();
163    int iLength = (int)str->length;
164    if (iLength == 0)
165    {
166        // Null string.
167        if (bufflen != 0) buff[0] = 0;
168        return 0;
169    }
170    const char *iPtr = str->chars;
171    // We can convert it directly using the maximum string length.
172    int space = MultiByteToWideChar(codePage, 0, iPtr, iLength, buff, (int)bufflen-1);
173
174    if (space <= 0)
175    {
176        if (bufflen != 0) buff[0] = 0;
177        return 0; // Error
178    }
179
180    buff[space] = 0; // Null terminate
181    return space;
182}
183
184WCHAR *Poly_string_to_U_alloc(PolyWord ps, size_t extraChars)
185{
186    PolyStringObject *str = (PolyStringObject *)ps.AsObjPtr();
187    int iLength = (int)str->length;
188    if (iLength == 0 && extraChars == 0) return _wcsdup(L"");
189    const char *iPtr = str->chars;
190
191    // Find the space required.
192    int chars = MultiByteToWideChar(codePage, 0, iPtr, iLength, NULL, 0);
193    if (chars <= 0) return _wcsdup(L"");
194    WCHAR *res = (WCHAR*)malloc((chars+1+extraChars) * sizeof(WCHAR));
195    if (res == 0) return 0;
196    chars = MultiByteToWideChar(codePage, 0, iPtr, iLength, res, chars);
197    res[chars] = 0;
198    return res;
199}
200
201// convert_string_list return a list of strings.
202// This converts Unicode strings.
203Handle convert_string_list(TaskData *mdTaskData, int count, WCHAR **strings)
204{
205    Handle saved = mdTaskData->saveVec.mark();
206    Handle list  = SAVE(ListNull);
207
208    // It's simplest to process the strings in reverse order */
209    for (int i = count - 1; 0 <= i; i--)
210    {
211        Handle value = SAVE(C_string_to_Poly(mdTaskData, strings[i]));
212        Handle next  = alloc_and_save(mdTaskData, SIZEOF(ML_Cons_Cell));
213
214        DEREFLISTHANDLE(next)->h = value->Word();
215        DEREFLISTHANDLE(next)->t = list->Word();
216
217        // reset save vector to stop it overflowing
218        mdTaskData->saveVec.reset(saved);
219        list = SAVE(next->Word());
220    }
221
222    return list;
223}
224
225TempString::~TempString()
226{
227    free(m_value);
228}
229
230#endif
231
232/* convert_string_list return a list of strings. */
233Handle convert_string_list(TaskData *mdTaskData, int count, char **strings)
234{
235    Handle saved = mdTaskData->saveVec.mark();
236    Handle list  = SAVE(ListNull);
237
238    // It's simplest to process the strings in reverse order */
239    for (int i = count - 1; 0 <= i; i--)
240    {
241        /*
242        The order of these declarations is important, becaue we don't
243        want to have make to make the cons cell mutable. This is only
244        safe if we promise to initialise it fully before the next
245        ML heap allocation. SPF 29/11/96
246        */
247        Handle value = SAVE(C_string_to_Poly(mdTaskData, strings[i]));
248        Handle next  = alloc_and_save(mdTaskData, SIZEOF(ML_Cons_Cell));
249
250        DEREFLISTHANDLE(next)->h = value->Word();
251        DEREFLISTHANDLE(next)->t = list->Word();
252
253        // reset save vector to stop it overflowing
254        mdTaskData->saveVec.reset(saved);
255        list = SAVE(next->Word());
256    }
257
258    return list;
259}
260
261/* Convert a string list to a vector of C strings. */
262char **stringListToVector(Handle list)
263{
264    int len = 0;
265    /* Find the length of the list */
266    for (PolyWord p = list->Word(); p != ListNull; p = ((ML_Cons_Cell*)p.AsObjPtr())->t) len++;
267    /* Allocate vector. */
268    char **vec = (char**)calloc(len+1, sizeof(char*));
269    /* Copy the strings and put them into the vector. */
270    len = 0;
271
272    PolyWord q = list->Word();
273    while (q != ListNull) {
274        ML_Cons_Cell *cell = (ML_Cons_Cell*)q.AsObjPtr();
275        vec[len++] = Poly_string_to_C_alloc(cell->h);
276        q = cell->t;
277    }
278    return vec;
279}
280
281/* Free the memory used by the vector. */
282void freeStringVector(char **vec)
283{
284    char **p;
285    if (vec == 0) return;
286    for (p = vec; *p != 0; p++) free(*p);
287    free(vec);
288}
289
290// Concatenate two strings.  Now used only internally in the RTS in process_env.cpp
291Handle strconcatc(TaskData *mdTaskData, Handle y, Handle x)
292/* Note: arguments are in the reverse order from Poly */
293{
294    POLYUNSIGNED xlen = DEREFSTRINGHANDLE(x)->length;
295    /* Don't concatenate with null strings */
296    if (xlen == 0) return y;
297
298    POLYUNSIGNED ylen = DEREFSTRINGHANDLE(y)->length;
299    if (ylen == 0) return x;
300
301    POLYUNSIGNED len = xlen + ylen;
302
303    /* Get store for combined string. Include rounding up to next word and
304    room for the length word and add in the flag. */
305    Handle result = alloc_and_save(mdTaskData, (len + sizeof(PolyWord)-1)/sizeof(PolyWord) + 1, F_BYTE_OBJ);
306
307    DEREFSTRINGHANDLE(result)->length = len;
308
309    /* Copy first string */
310    char *to_ptr = DEREFSTRINGHANDLE(result)->chars;
311    char *from_ptr = DEREFSTRINGHANDLE(x)->chars;
312    while (xlen-- > 0) (*to_ptr++ = *from_ptr++);
313
314    /* Add on second */
315    from_ptr = DEREFSTRINGHANDLE(y)->chars;
316    while (ylen-- > 0) (*to_ptr++ = *from_ptr++);
317
318    return(result);
319} /* strconcat */
320
321// Only used in Xwindows and then only for debugging.
322void print_string(PolyWord s)
323{
324    extern FILE *polyStdout;
325        PolyStringObject * str = (PolyStringObject *)s.AsObjPtr();
326        fwrite(str->chars, 1, str->length, polyStdout);
327}
328