14 #if !defined(ECERE_BOOTSTRAP) // quick fix for now
15 #if defined(__WIN32__)
16 #define WIN32_LEAN_AND_MEAN
17 #define String _String
32 default extern Platform runtimePlatform;
34 #define IS_ALUNDER(ch) ((ch) == '_' || isalnum((ch)))
36 public define DIR_SEP = (__runtimePlatform == win32) ? '\\' : '/';
37 public define DIR_SEPS = (__runtimePlatform == win32) ? "\\" : "/";
39 // Maximum length for a vsnprintf string
40 public define MAX_F_STRING = 1025;
42 // Maximum length for a directories and filenames strings
43 public define MAX_EXTENSION = 17; // 16 + \0
44 public define MAX_FILENAME = 274; // 256 chars + extension + dot + \0
45 public define MAX_DIRECTORY = 534; // 8 levels + 8 separators + \0
46 public define MAX_LOCATION = 797; // directory + filename + separator + \0
48 // --- File related String functions ---
49 public char * GetExtension(const char * string, char * output)
52 int len = strlen(string);
53 int limit = Max(0, len-MAX_EXTENSION);
55 for(c = len; c>=limit; c--)
60 strcpy(output, string+c+1);
63 else if(ch == '/' || ch == '\\')
69 public char * StripLastDirectory(const char * string, char * output)
72 if(runtimePlatform == win32 && !strcmp(string, "\\\\"))
79 int len = strlen(string);
80 for(c = len-2; c>=0; c--)
81 if(string[c] == '/' || string[c] == '\\')
83 else if(string[c] == '>' || (string[c] == ':' && c == 0))
89 if((runtimePlatform == win32) ? (c >= 0) : (c > 0))
91 memmove(output, string, c);
94 if(runtimePlatform == win32 && c == 1 && output[0] == '\\' && output[1] == '\\')
100 strcpy(output, DIR_SEPS);
105 // Return root on UNIX instead of null...
108 strcpy(output, DIR_SEPS);
120 public char * SplitDirectory(const char * string, char * part, char * rest)
125 for(;(ch = string[c]) && (ch == '/' || ch == '\\'); c++);
128 part[len++] = DIR_SEP;
131 for(;(ch = string[c]) && (ch != '/' && ch != '\\'); c++)
133 if(len < MAX_FILENAME)
138 for(;(ch = string[c]) && (ch == '/' || ch == '\\'); c++);
139 memmove(rest, string + c, strlen(string + c) + 1);
140 for(c = strlen(rest); c >= 0; c--)
141 if(ch != '/' && ch != '\\')
150 public char * GetLastDirectory(const char * string, char * output)
153 int len = string ? strlen(string) : 0;
154 for(c = len-2; c>=0; c--)
155 if(string[c] == '/' || string[c] == '\\' || string[c] == ':' || string[c] == '>')
160 memmove(output, string+c, strlen(string+c)+1);
164 len = strlen(output);
165 if(len > 1 && (output[len-1] == '\\' || output[len-1] == '/'))
166 output[len-1] = '\0';
170 public bool SplitArchivePath(const char * fileName, char * archiveName, const char * * archiveFile)
173 if(fileName[0] == '<')
175 int c = strlen(fileName);
176 for(;c>0 && fileName[c] != '>'; c--);
179 strncpy(archiveName, fileName + 1, c - 1);
180 archiveName[c - 1] = '\0';
181 *archiveFile = fileName + c + 1;
185 else if(fileName[0] == ':')
187 strcpy(archiveName, ":");
188 *archiveFile = fileName + 1;
194 public char * PathCatSlash(char * string, const char * addedPath)
196 bool modified = false;
199 char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "";
200 const char * file = null;
203 bool isArchive = SplitArchivePath(string, archiveName, &file);
204 char * urlFileName = null;
205 char * protocolSymbol;
207 strcpy(fileName, isArchive ? file : string);
209 if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
211 protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
214 char * slash = strstr(protocolSymbol + 3, "/");
219 urlFileName = fileName + strlen(fileName);
223 protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
226 int len = protocolSymbol - addedPath + 3;
227 memcpy(fileName, addedPath, len);
232 else if(__runtimePlatform == win32)
234 if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
236 fileName[0] = (char)toupper(addedPath[0]);
242 else if(addedPath[0] == '\\' && addedPath[1] == '\\')
244 fileName[0] = fileName[1] = '\\';
249 // A drive needs to be selected
250 /* TOCHECK: Cutting this out, can't handle relative path
251 else if(fileName[0] == '/' && !archiveName[0] && strcmp(addedPath, "/"))
256 if(!modified && isURL && (addedPath[0] == '\\' || addedPath[0] == '/'))
258 urlFileName[0] = '/';
259 urlFileName[1] = '\0';
261 else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
263 if(__runtimePlatform == win32)
266 if(addedPath[0] == '/' && !addedPath[1])
268 fileName[0] = addedPath[0];
273 else if(fileName[0] && fileName[1] == ':')
278 // Relative path root of drive
295 for(; addedPath[c]; )
297 // DANGER OF OVERFLOW HERE
298 // char directory[MAX_FILENAME];
299 char directory[MAX_FILENAME * 16];
304 for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
305 for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
307 if(isURL && ch == '?')
311 if(len < MAX_FILENAME)
312 directory[len++] = ch;
314 directory[len] = '\0';
316 // Trim rightmost spaces
317 for(count = len-1; count >= 0 && (directory[count] == ' ' || directory[count] == '\t'); count--)
319 directory[count] = '\0';
326 if(strstr(directory, "..") == directory && (!directory[2] || directory[2] == DIR_SEP || directory[2] == '/'))
328 int strLen = strlen(fileName) - 1;
331 // Go back one directory
332 for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--);
333 for(;strLen > -1 && (ch = fileName[strLen]) && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
334 for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--);
338 strLen = Max(strLen, urlFileName - fileName);
340 if(!strcmp(fileName + strLen + 1, ".."))
342 strcat(fileName, "/" /*DIR_SEPS*/);
343 strcat(fileName, "..");
347 if(__runtimePlatform == win32)
349 if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
361 fileName[strLen+1] = '\0';
365 fileName[strLen+1] = '\0';
377 strcpy(fileName, "..");
380 else if(strcmp(directory, "."))
382 int strLen = strlen(fileName);
383 bool notZeroLen = strLen > 0;
384 // if(strLen > 1 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
385 if(strLen > 0 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
387 if(notZeroLen /*&& fileName[strLen-1] != ':' && fileName[strLen-1] != '>'*/)
388 fileName[strLen++] = '/';
390 fileName[strLen] = '\0';
392 if(strLen + strlen(directory) > MAX_LOCATION - 3)
393 return null; // AN ERROR OCCURRED!
395 strcat(fileName, directory);
398 if(isURL && ch == '/')
399 strcat(fileName, "/");
400 if(isURL && ch == '?')
402 strcat(fileName, addedPath+c);
407 sprintf(string, "<%s>%s", archiveName, fileName);
409 strcpy(string, fileName);
411 return modified ? string : null;
414 public char * PathCat(char * string, const char * addedPath)
416 bool modified = false;
419 char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "";
420 const char * file = null;
423 bool isArchive = SplitArchivePath(string, archiveName, &file);
424 char * urlFileName = null;
425 char * protocolSymbol;
427 strcpy(fileName, isArchive ? file : string);
429 if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
431 protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
434 char * slash = strstr(protocolSymbol + 3, "/");
439 urlFileName = fileName + strlen(fileName);
443 protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
446 int len = protocolSymbol - addedPath + 3;
447 memcpy(fileName, addedPath, len);
452 else if(runtimePlatform == win32)
454 if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
456 fileName[0] = (char)toupper(addedPath[0]);
462 else if(addedPath[0] == '\\' && addedPath[1] == '\\')
464 fileName[0] = fileName[1] = '\\';
469 // A drive needs to be selected
470 else if(fileName[0] == '/' && !archiveName[0] && strcmp(addedPath, "/"))
474 if(!modified && isURL && (addedPath[0] == '\\' || addedPath[0] == '/'))
476 urlFileName[0] = '/';
477 urlFileName[1] = '\0';
479 else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
481 if(runtimePlatform == win32)
484 if(addedPath[0] == '/' && !addedPath[1])
486 fileName[0] = addedPath[0];
491 else if(fileName[0] && fileName[1] == ':')
496 // Relative path root of drive
513 for(; addedPath[c]; )
515 // DANGER OF OVERFLOW HERE
516 // char directory[MAX_FILENAME];
517 char directory[MAX_FILENAME * 16];
522 for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
523 for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
525 if(isURL && ch == '?')
529 if(len < MAX_FILENAME)
530 directory[len++] = ch;
532 directory[len] = '\0';
534 // Trim rightmost spaces
535 for(count = len-1; count >= 0 && (directory[count] == ' ' || directory[count] == '\t'); count--)
537 directory[count] = '\0';
544 if(strstr(directory, "..") == directory && (!directory[2] || directory[2] == DIR_SEP))
546 int strLen = strlen(fileName) - 1;
549 bool separator = false;
551 // Go back one directory
552 for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--);
553 for(;strLen > -1 && (ch = fileName[strLen]) && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
554 for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--) separator = true;
558 strLen = Max(strLen, urlFileName - fileName);
560 if(!strcmp(fileName + strLen + (separator ? 2 : 1), ".."))
562 strcat(fileName, DIR_SEPS);
563 strcat(fileName, "..");
567 if(runtimePlatform == win32)
569 if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
581 fileName[strLen+1] = '\0';
585 fileName[strLen+1] = '\0';
597 strcpy(fileName, "..");
600 else if(strcmp(directory, "."))
602 int strLen = strlen(fileName);
603 bool notZeroLen = strLen > 0;
604 // if(strLen > 1 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
605 if(strLen > 0 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
607 if(notZeroLen /*&& fileName[strLen-1] != ':' && fileName[strLen-1] != '>'*/)
610 fileName[strLen++] = '/';
612 fileName[strLen++] = DIR_SEP;
615 fileName[strLen] = '\0';
617 if(strLen + strlen(directory) > MAX_LOCATION - 3)
618 return null; // AN ERROR OCCURRED!
620 strcat(fileName, directory);
623 if(isURL && ch == '/')
624 strcat(fileName, "/");
625 if(isURL && ch == '?')
627 strcat(fileName, addedPath+c);
632 sprintf(string, "<%s>%s", archiveName, fileName);
634 strcpy(string, fileName);
636 return modified ? string : null;
639 public char * MakePathRelative(const char * path, const char * to, char * destination)
642 // Don't process empty paths
644 memmove(destination, path, strlen(path)+1);
647 // TOFIX: DANGER OF OVERFLOW HERE
648 char pathPart[MAX_FILENAME * 16], pathRest[MAX_LOCATION];
649 char toPart[MAX_FILENAME * 16], toRest[MAX_LOCATION];
650 bool different = false;
652 strcpy(pathRest, path);
655 destination[0] = '\0';
658 SplitDirectory(toRest, toPart, toRest);
660 SplitDirectory(pathRest, pathPart, pathRest);
662 if(different || fstrcmp(toPart, pathPart))
665 strcat(destination, "..");
666 strcat(destination, DIR_SEPS);
671 PathCat(destination, pathPart);
674 SplitDirectory(pathRest, pathPart, pathRest);
675 PathCat(destination, pathPart);
678 len = strlen(destination);
679 if(len>1 && (destination[len-1] == '/' || destination[len-1] == '\\'))
680 destination[--len] = '\0';
684 public bool StripExtension(char * string)
687 for(c = strlen(string); c>=0; c--)
693 else if(string[c] == '\\' || string[c] == '/')
698 public char * ChangeExtension(const char * string, const char * ext, char * output)
701 strcpy(output, string);
702 StripExtension(output);
709 // --- String Stuff (Temporarily outside String class) ---
710 public void PrintSize(char * string, uint size, int prec)
715 sprintf(format, "%%.0%df", prec);
716 if(size > 1024 * 1024 * 1024)
718 sprintf(string, format, size / (float)(1024 * 1024 * 1024));
719 strcat(string, " GB");
721 else if(size > 1024 * 1024)
723 sprintf(string, format, size / (float)(1024 * 1024));
724 strcat(string, " MB");
728 sprintf(string, format, size / (float)1024);
729 strcat(string, " KB");
733 sprintf(string, "%d B", size);
736 public void PrintBigSize(char * string, double size, int prec)
741 sprintf(format, "%%.0%df", prec);
742 if(size > 1024.0 * 1024.0 * 1024.0 * 1024.0)
744 sprintf(string, format, size / (1024 * 1024 * 1024.0 * 1024.0));
745 strcat(string, " TB");
747 else if(size > 1024.0 * 1024.0 * 1024.0)
749 sprintf(string, format, size / (1024.0 * 1024.0 * 1024.0));
750 strcat(string, " GB");
752 else if(size > 1024.0 * 1024.0)
754 sprintf(string, format, size / (1024.0 * 1024.0));
755 strcat(string, " MB");
759 sprintf(string, format, size / 1024.0);
760 strcat(string, " KB");
764 sprintf(string, "%.0f B", size);
767 public char * SearchString(const char * buffer, int start, const char * subStr, bool matchCase, bool matchWord)
772 const char * strBuffer = buffer + start;
773 int subLen = strlen(subStr);
774 char beforeChar = start ? *(strBuffer-1) : 0;
775 int (*strcompare)(const char *, const char *, uintsize) = matchCase ? strncmp : strnicmp;
777 for(ptr = strBuffer; *ptr; ptr++)
779 if(matchCase ? (*subStr == *ptr) : (tolower(*subStr) == tolower(*ptr)))
783 if(!strcompare(ptr,subStr,subLen) &&
785 !IS_ALUNDER(ptr[subLen]) &&
786 !IS_ALUNDER(beforeChar))
788 (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr[subLen])) &&
789 (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(beforeChar)))
794 if(!strcompare(ptr,subStr,subLen))
804 public char * RSearchString(const char * buffer, const char * subStr, int maxLen, bool matchCase, bool matchWord)
808 int subLen = strlen(subStr);
809 const char * ptr1 = buffer + maxLen - subLen;
810 const char * ptr2 = buffer + maxLen - subLen - 1;
811 int (*strcompare)(const char *, const char *, uintsize) = matchCase ? strncmp : strnicmp;
812 for(; ptr1 >=buffer; ptr1--, ptr2--)
814 if(tolower(*subStr) == tolower(*ptr1))
818 if(!strcompare(ptr1,subStr,subLen) &&
819 //!IS_ALUNDER(ptr1[subLen]) && !IS_ALUNDER(*ptr2))
820 (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr1[subLen])) &&
821 (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(*ptr2)))
827 if(!strcompare(ptr1,subStr,subLen))
836 //public define gnuMakeCharsNeedEscaping = "$%";
837 //public define windowsFileNameCharsNotAllowed = "*/:<>?\\\"|";
838 //public define linuxFileNameCharsNotAllowed = "/";
839 //public define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
840 //public define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
842 // fix #139 to remove " = 2" and warnings for backward compatible calls to Tokenize using 'true' for the 'esc' argument;
843 public enum BackSlashEscaping : bool { forArgsPassing = 2 };
844 public int Tokenize(char * string, int maxTokens, char* tokens[], BackSlashEscaping esc)
846 const char * escChars, * escCharsQuoted;
848 bool quoted = false, escaped = false;
849 char * start = null, * output = string;
851 if(__runtimePlatform == win32)
853 //define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
854 escChars = " !\"%&'()+,;=[]^`{}~"; // windowsFileNameCharsNeedEscaping;
855 escCharsQuoted = "\"";
859 //define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
860 escChars = " !\"$&'()*:;<=>?[\\`{|"; // linuxFileNameCharsNeedEscaping;
861 escCharsQuoted = "\"()$";
864 for(; (ch = *string) && count<maxTokens; string++, output++)
866 bool wasEscaped = escaped;
882 else if(ch == ' ' && !quoted)
884 tokens[count++] = start;
899 if(!wasEscaped && ch == '\\' && ( esc == true || (esc == forArgsPassing && strchr(quoted ? escCharsQuoted : escChars, *(string+1))) ))
902 if(start && count < maxTokens)
904 tokens[count++] = start;
910 public int TokenizeWith(char * string, int maxTokens, char* tokens[], const char * tokenizers, bool escapeBackSlashes)
915 bool escaped = false;
916 char * output = string;
917 bool quotedFromStart = false;
919 for(; *string && count < maxTokens; string++, output++)
932 else if(escapeBackSlashes && *string == '\\')
934 else if(*string == '\"')
940 quotedFromStart = false;
946 else if(strchr(tokenizers, *string) && !quoted)
948 tokens[count++] = start;
952 // MOVED THIS INSIDE IF ABOVE...
953 //if(output != string)
954 // *output = *string;
956 else if(!strchr(tokenizers, *string))
960 quotedFromStart = true;
967 if(*string == '\\' && escapeBackSlashes)
972 if(start && count < maxTokens)
974 tokens[count++] = start;
980 public char * TrimLSpaces(const char * string, char * output)
983 for(c = 0; string[c] && string[c] == ' '; c++);
984 memmove(output, string + c, strlen(string+c)+1);
988 public char * TrimRSpaces(const char * string, char * output)
991 for(c = strlen(string)-1; c >= 0 && string[c] == ' '; c--);
994 memmove(output, string, c+1);
1002 public void ChangeCh(char * string, char ch1, char ch2)
1005 for(c=0; string[c]; c++)
1006 if(string[c] == ch1) string[c] = ch2;
1009 public void ChangeChars(char * string, const char * chars, char alt)
1012 for(c=0; string[c]; c++)
1013 if(strchr(chars, string[c])) string[c] = alt;
1016 public void RepeatCh(char * string, int count, char ch)
1019 for(c=0; c<count; c++)
1024 public char * CopyString(const char * string)
1028 int len = strlen(string);
1029 char * destination = new char[len+1];
1031 memcpy(destination, string, len + 1);
1038 public bool GetString(char ** buffer, char * string, int max)
1042 bool quoted = false;
1044 if(!**buffer) { string[0]=0; return false; }
1048 if(!(ch = *((*buffer)++)))
1050 if( (ch!='\n') && (ch!='\r') && (ch!=' ') && (ch!=',') && (ch!='\t'))
1052 if(!*(*buffer)) break;
1056 for(c=0; c<max-1; c++)
1058 if(!quoted && ((ch=='\n')||(ch=='\r')||(ch==' ')||(ch==',')||(ch=='\t')))
1071 if(!(ch = *(*buffer)))
1084 public int GetValue(char ** buffer)
1087 GetString(buffer,string,20);
1088 return atoi(string);
1091 public uint GetHexValue(char ** buffer)
1094 GetString(buffer,string,20);
1095 return (uint)strtoul(string, null, 16);
1098 public char * StripQuotes(const char * string, char * output)
1101 const char * src = (string[0] == '\"') ? (string+1) : string;
1102 memmove(output, src, strlen(src)+1);
1103 len = strlen(output);
1104 if(len && output[len-1] == '\"')
1105 output[len-1] = '\0';
1109 public double FloatFromString(const char * string)
1112 float dec = 0,res = 0;
1115 for(c = 0; string[c]; c++)
1118 if(ch == ' ') continue;
1121 if(neg == -1) break;
1124 else if((ch == '.') && !dec)
1126 else if(isdigit(ch))
1135 res = res * 10 + dig;
1143 public bool IsPathInsideOf(const char * path, const char * of)
1145 if(!path[0] || !of[0])
1146 return false; // What to do here? Ever used?
1149 char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION];
1150 char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION];
1152 strcpy(pathRest, path);
1153 for(; ofRest[0] && pathRest[0];)
1155 SplitDirectory(ofRest, ofPart, ofRest);
1156 SplitDirectory(pathRest, pathPart, pathRest);
1157 if(fstrcmp(pathPart, ofPart))
1160 if(!ofRest[0] && !pathRest[0]) // paths are identical - should return false or true? (changed to false)
1162 else if(!pathRest[0]) // not inside of, it's the other way around
1168 public enum StringAllocType { pointer, stack, heap };
1169 public class ZString
1174 StringAllocType allocType;
1186 if(allocType == heap)
1190 void copyString(char * value, int newLen)
1192 if(allocType == pointer)
1198 if(allocType == heap)
1200 int newSize = newLen ? newLen + 1 : 0;
1203 if(newSize < minSize) newSize = minSize;
1204 else if(newSize > maxSize) newSize = maxSize;
1207 _string = renew _string char[newSize];
1209 _string = new char[newSize];
1215 if(newLen + 1 > size) newLen = size-1;
1220 memcpy(_string, value, newLen);
1221 _string[newLen] = 0;
1227 const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
1232 bool OnGetDataFromString(const char * string)
1234 property::string = (char *)string;
1238 property char * string
1240 set { copyString(value, value ? strlen(value) : 0); }
1241 get { return _string; }
1244 property const char *
1246 get { return _string; }
1251 len = value ? strlen(value) : 0;
1252 _string = (char *)value;
1253 allocType = pointer;
1258 void concatf(const char * format, ...)
1260 if(format && allocType != pointer)
1264 va_start(args, format);
1267 _string = renew _string char[minSize];
1270 addedLen = vsnprintf(string + len, Max(0, size - 1 - len), format, args);
1280 void concat(ZString s)
1282 if(s && allocType != pointer)
1284 int addedLen = s.len;
1285 int newLen = len + addedLen;
1286 if(allocType == heap && newLen + 1 > size)
1288 int newSize = newLen + 1;
1289 if(newSize > maxSize)
1293 _string = renew _string char[newLen];
1297 if(newLen + 1 > size)
1298 addedLen = size - 1 - len;
1301 memcpy(_string + len, s._string, addedLen);
1308 void copy(ZString s)
1310 copyString(s._string, s.len);