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