ecere: Initial Emscripten support
[sdk] / ecere / src / com / String.ec
1 #define _Noreturn
2
3 namespace sys;
4
5 #define set _set
6 #define uint _uint
7 #define strlen _strlen
8
9 default:
10
11 #undef __BLOCKS__
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #if !defined(ECERE_BOOTSTRAP) // quick fix for now
15 #if defined(__WIN32__)
16 #define WIN32_LEAN_AND_MEAN
17 #define String _String
18 #include <windows.h>
19 #undef String
20 #else
21 #include <unistd.h>
22 #endif
23 #endif
24
25 #undef uint
26 #undef set
27 #undef strlen
28 private:
29
30 import "instance"
31
32 default extern Platform runtimePlatform;
33
34 #define IS_ALUNDER(ch) ((ch) == '_' || isalnum((ch)))
35
36 public define DIR_SEP   = (__runtimePlatform == win32) ? '\\' : '/';
37 public define DIR_SEPS  = (__runtimePlatform == win32) ? "\\" : "/";
38
39 // Maximum length for a vsnprintf string
40 public define MAX_F_STRING = 1025;
41
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
47
48 // --- File related String functions ---
49 public char * GetExtension(const char * string, char * output)
50 {
51    int c;
52    int len = strlen(string);
53    int limit = Max(0, len-MAX_EXTENSION);
54    output[0] = '\0';
55    for(c = len; c>=limit; c--)
56    {
57       char ch = string[c];
58       if(ch == '.')
59       {
60          strcpy(output, string+c+1);
61          break;
62       }
63       else if(ch == '/' || ch == '\\')
64          break;
65    }
66    return output;
67 }
68
69 public char * StripLastDirectory(const char * string, char * output)
70 {
71    int c;
72    if(runtimePlatform == win32 && !strcmp(string, "\\\\"))
73    {
74       strcpy(output, "/");
75       return output;
76    }
77    else
78    {
79       int len = strlen(string);
80       for(c = len-2; c>=0; c--)
81          if(string[c] == '/' || string[c] == '\\')
82             break;
83          else if(string[c] == '>' || (string[c] == ':' && c == 0))
84          {
85             c++;
86             break;
87          }
88
89       if((runtimePlatform == win32) ? (c >= 0) : (c > 0))
90       {
91          memmove(output, string, c);
92          if(c > 0)
93          {
94             if(runtimePlatform == win32 && c == 1 && output[0] == '\\' && output[1] == '\\')
95                output[2] = '\0';
96             else
97                output[c] = '\0';
98          }
99          else
100             strcpy(output, DIR_SEPS);
101          return output;
102       }
103       else
104       {
105          // Return root on UNIX instead of null...
106          if(c == 0)
107          {
108             strcpy(output, DIR_SEPS);
109             return output;
110          }
111          else
112          {
113             strcpy(output, "");
114             return null;
115          }
116       }
117    }
118 }
119
120 public char * SplitDirectory(const char * string, char * part, char * rest)
121 {
122    int len = 0;
123    char ch;
124    int c = 0;
125    for(;(ch = string[c]) && (ch == '/' || ch == '\\'); c++);
126
127    if(c)
128       part[len++] = DIR_SEP;
129    else
130    {
131       for(;(ch = string[c]) && (ch != '/' && ch != '\\'); c++)
132       {
133          if(len < MAX_FILENAME)
134             part[len++] = ch;
135       }
136    }
137
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 != '\\')
142          break;
143    if(c > 0)
144       rest[c] = '\0';
145
146    part[len] = '\0';
147    return rest;
148 }
149
150 public char * GetLastDirectory(const char * string, char * output)
151 {
152    int c;
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] == '>')
156          break;
157
158    c++;
159    if(c >= 0)
160       memmove(output, string+c, strlen(string+c)+1);
161    else
162       output[0] = '\0';
163
164    len = strlen(output);
165    if(len > 1 && (output[len-1] == '\\' || output[len-1] == '/'))
166       output[len-1] = '\0';
167    return output;
168 }
169
170 public bool SplitArchivePath(const char * fileName, char * archiveName, const char * * archiveFile)
171 {
172    // Support Archives
173    if(fileName[0] == '<')
174    {
175       int c = strlen(fileName);
176       for(;c>0 && fileName[c] != '>'; c--);
177       if(c > 0)
178       {
179          strncpy(archiveName, fileName + 1, c - 1);
180          archiveName[c - 1] = '\0';
181          *archiveFile = fileName + c + 1;
182          return true;
183       }
184    }
185    else if(fileName[0] == ':')
186    {
187       strcpy(archiveName, ":");
188       *archiveFile = fileName + 1;
189       return true;
190    }
191    return false;
192 }
193
194 public char * PathCatSlash(char * string, const char * addedPath)
195 {
196    bool modified = false;
197    if(addedPath)
198    {
199       char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "";
200       const char * file = null;
201       int c = 0;
202       bool isURL = false;
203       bool isArchive = SplitArchivePath(string, archiveName, &file);
204       char * urlFileName = null;
205       char * protocolSymbol;
206
207       strcpy(fileName, isArchive ? file : string);
208
209       if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
210       {
211          protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
212          if(protocolSymbol)
213          {
214             char * slash = strstr(protocolSymbol + 3, "/");
215             isURL = true;
216             if(slash)
217                urlFileName = slash;
218             else
219                urlFileName = fileName + strlen(fileName);
220          }
221       }
222
223       protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
224       if(protocolSymbol)
225       {
226          int len = protocolSymbol - addedPath + 3;
227          memcpy(fileName, addedPath, len);
228          fileName[len] = 0;
229          isURL = true;
230          c = len;
231       }
232       else if(__runtimePlatform == win32)
233       {
234          if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
235          {
236             fileName[0] = (char)toupper(addedPath[0]);
237             fileName[1] = ':';
238             fileName[2] = '\0';
239             c = 2;
240             modified = true;
241          }
242          else if(addedPath[0] == '\\' && addedPath[1] == '\\')
243          {
244             fileName[0] = fileName[1] = '\\';
245             fileName[2] = '\0';
246             c = 2;
247             modified = true;
248          }
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, "/"))
252             return null;
253          */
254       }
255
256       if(!modified && isURL && (addedPath[0] == '\\' || addedPath[0] == '/'))
257       {
258          urlFileName[0] = '/';
259          urlFileName[1] = '\0';
260       }
261       else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
262       {
263          if(__runtimePlatform == win32)
264          {
265             // Entire Computer
266             if(addedPath[0] == '/' && !addedPath[1])
267             {
268                fileName[0] = addedPath[0];
269                fileName[1] = '\0';
270                modified = true;
271             }
272             // Root of drive
273             else if(fileName[0] && fileName[1] == ':')
274             {
275                fileName[2] = '\0';
276                modified = true;
277             }
278             // Relative path root of drive
279             else
280             {
281                fileName[0] = '\\';
282                fileName[1] = '\0';
283                modified = true;
284             }
285          }
286          else
287          {
288             fileName[0] = '/';
289             fileName[1] = '\0';
290             modified = true;
291          }
292          c = 1;
293       }
294
295       for(; addedPath[c]; )
296       {
297          // DANGER OF OVERFLOW HERE
298          // char directory[MAX_FILENAME];
299          char directory[MAX_FILENAME * 16];
300          int len = 0;
301          char ch;
302          int count;
303
304          for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
305          for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
306          {
307             if(isURL && ch == '?')
308             {
309                break;
310             }
311             if(len < MAX_FILENAME)
312                directory[len++] = ch;
313          }
314          directory[len] = '\0';
315
316          // Trim rightmost spaces
317          for(count = len-1; count >= 0 && (directory[count] == ' ' || directory[count] == '\t'); count--)
318          {
319             directory[count] = '\0';
320             len--;
321          }
322
323          if(len > 0)
324          {
325             modified = true;
326             if(strstr(directory, "..") == directory && (!directory[2] || directory[2] == DIR_SEP || directory[2] == '/'))
327             {
328                int strLen = strlen(fileName) - 1;
329                if(strLen > -1)
330                {
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--);
335
336                   if(isURL)
337                   {
338                      strLen = Max(strLen, urlFileName - fileName);
339                   }
340                   if(!strcmp(fileName + strLen + 1, ".."))
341                   {
342                      strcat(fileName, "/" /*DIR_SEPS*/);
343                      strcat(fileName, "..");
344                   }
345                   else
346                   {
347                      if(__runtimePlatform == win32)
348                      {
349                         if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
350                         {
351                            if(!fileName[2])
352                               return null;
353                            else
354                            {
355                               fileName[0] = '\\';
356                               fileName[1] = '\\';
357                               fileName[2] = '\0';
358                            }
359                         }
360                         else
361                            fileName[strLen+1] = '\0';
362                      }
363                      else
364                      {
365                         fileName[strLen+1] = '\0';
366                         if(strLen<0)
367                         {
368                            fileName[0] = '/';
369                            fileName[1] = '\0';
370                            strLen = 2;
371                         }
372                      }
373                   }
374                }
375                else
376                {
377                   strcpy(fileName, "..");
378                }
379             }
380             else if(strcmp(directory, "."))
381             {
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] == '\\'))
386                   strLen--;
387                if(notZeroLen /*&& fileName[strLen-1] != ':' && fileName[strLen-1] != '>'*/)
388                   fileName[strLen++] = '/';
389
390                fileName[strLen] = '\0';
391
392                if(strLen + strlen(directory) > MAX_LOCATION - 3)
393                   return null;   // AN ERROR OCCURRED!
394
395                strcat(fileName, directory);
396             }
397          }
398          if(isURL && ch == '/')
399             strcat(fileName, "/");
400          if(isURL && ch == '?')
401          {
402             strcat(fileName, addedPath+c);
403             break;
404          }
405       }
406       if(archiveName[0])
407          sprintf(string, "<%s>%s", archiveName, fileName);
408       else
409          strcpy(string, fileName);
410    }
411    return modified ? string : null;
412 }
413
414 public char * PathCat(char * string, const char * addedPath)
415 {
416    bool modified = false;
417    if(addedPath)
418    {
419       char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "";
420       const char * file = null;
421       int c = 0;
422       bool isURL = false;
423       bool isArchive = SplitArchivePath(string, archiveName, &file);
424       char * urlFileName = null;
425       char * protocolSymbol;
426
427       strcpy(fileName, isArchive ? file : string);
428
429       if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
430       {
431          protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
432          if(protocolSymbol)
433          {
434             char * slash = strstr(protocolSymbol + 3, "/");
435             isURL = true;
436             if(slash)
437                urlFileName = slash;
438             else
439                urlFileName = fileName + strlen(fileName);
440          }
441       }
442
443       protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
444       if(protocolSymbol)
445       {
446          int len = protocolSymbol - addedPath + 3;
447          memcpy(fileName, addedPath, len);
448          fileName[len] = 0;
449          isURL = true;
450          c = len;
451       }
452       else if(runtimePlatform == win32)
453       {
454          if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
455          {
456             fileName[0] = (char)toupper(addedPath[0]);
457             fileName[1] = ':';
458             fileName[2] = '\0';
459             c = 2;
460             modified = true;
461          }
462          else if(addedPath[0] == '\\' && addedPath[1] == '\\')
463          {
464             fileName[0] = fileName[1] = '\\';
465             fileName[2] = '\0';
466             c = 2;
467             modified = true;
468          }
469          // A drive needs to be selected
470          else if(fileName[0] == '/' && !archiveName[0] && strcmp(addedPath, "/"))
471             return null;
472       }
473
474       if(!modified && isURL && (addedPath[0] == '\\' || addedPath[0] == '/'))
475       {
476          urlFileName[0] = '/';
477          urlFileName[1] = '\0';
478       }
479       else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
480       {
481          if(runtimePlatform == win32)
482          {
483             // Entire Computer
484             if(addedPath[0] == '/' && !addedPath[1])
485             {
486                fileName[0] = addedPath[0];
487                fileName[1] = '\0';
488                modified = true;
489             }
490             // Root of drive
491             else if(fileName[0] && fileName[1] == ':')
492             {
493                fileName[2] = '\0';
494                modified = true;
495             }
496             // Relative path root of drive
497             else
498             {
499                fileName[0] = '\\';
500                fileName[1] = '\0';
501                modified = true;
502             }
503          }
504          else
505          {
506             fileName[0] = '/';
507             fileName[1] = '\0';
508             modified = true;
509          }
510          c = 1;
511       }
512
513       for(; addedPath[c]; )
514       {
515          // DANGER OF OVERFLOW HERE
516          // char directory[MAX_FILENAME];
517          char directory[MAX_FILENAME * 16];
518          int len = 0;
519          char ch;
520          int count;
521
522          for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
523          for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
524          {
525             if(isURL && ch == '?')
526             {
527                break;
528             }
529             if(len < MAX_FILENAME)
530                directory[len++] = ch;
531          }
532          directory[len] = '\0';
533
534          // Trim rightmost spaces
535          for(count = len-1; count >= 0 && (directory[count] == ' ' || directory[count] == '\t'); count--)
536          {
537             directory[count] = '\0';
538             len--;
539          }
540
541          if(len > 0)
542          {
543             modified = true;
544             if(strstr(directory, "..") == directory && (!directory[2] || directory[2] == DIR_SEP))
545             {
546                int strLen = strlen(fileName) - 1;
547                if(strLen > -1)
548                {
549                   bool separator = false;
550
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;
555
556                   if(isURL)
557                   {
558                      strLen = Max(strLen, urlFileName - fileName);
559                   }
560                   if(!strcmp(fileName + strLen + (separator ? 2 : 1), ".."))
561                   {
562                      strcat(fileName, DIR_SEPS);
563                      strcat(fileName, "..");
564                   }
565                   else
566                   {
567                      if(runtimePlatform == win32)
568                      {
569                         if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
570                         {
571                            if(!fileName[2])
572                               return null;
573                            else
574                            {
575                               fileName[0] = '\\';
576                               fileName[1] = '\\';
577                               fileName[2] = '\0';
578                            }
579                         }
580                         else
581                            fileName[strLen+1] = '\0';
582                      }
583                      else
584                      {
585                         fileName[strLen+1] = '\0';
586                         if(strLen<0)
587                         {
588                            fileName[0] = '/';
589                            fileName[1] = '\0';
590                            strLen = 2;
591                         }
592                      }
593                   }
594                }
595                else
596                {
597                   strcpy(fileName, "..");
598                }
599             }
600             else if(strcmp(directory, "."))
601             {
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] == '\\'))
606                   strLen--;
607                if(notZeroLen /*&& fileName[strLen-1] != ':' && fileName[strLen-1] != '>'*/)
608                {
609                   if(isURL)
610                      fileName[strLen++] = '/';
611                   else
612                      fileName[strLen++] = DIR_SEP;
613                }
614
615                fileName[strLen] = '\0';
616
617                if(strLen + strlen(directory) > MAX_LOCATION - 3)
618                   return null;   // AN ERROR OCCURRED!
619
620                strcat(fileName, directory);
621             }
622          }
623          if(isURL && ch == '/')
624             strcat(fileName, "/");
625          if(isURL && ch == '?')
626          {
627             strcat(fileName, addedPath+c);
628             break;
629          }
630       }
631       if(archiveName[0])
632          sprintf(string, "<%s>%s", archiveName, fileName);
633       else
634          strcpy(string, fileName);
635    }
636    return modified ? string : null;
637 }
638
639 public char * MakePathRelative(const char * path, const char * to, char * destination)
640 {
641    int len;
642    // Don't process empty paths
643    if(!path[0])
644       memmove(destination, path, strlen(path)+1);
645    else
646    {
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;
651
652       strcpy(pathRest, path);
653       strcpy(toRest, to);
654
655       destination[0] = '\0';
656       for(;toRest[0];)
657       {
658          SplitDirectory(toRest, toPart, toRest);
659          if(!different)
660             SplitDirectory(pathRest, pathPart, pathRest);
661
662          if(different || fstrcmp(toPart, pathPart))
663          {
664             different = true;
665             strcat(destination, "..");
666             strcat(destination, DIR_SEPS);
667          }
668       }
669
670       if(different)
671          PathCat(destination, pathPart);
672       for(;pathRest[0];)
673       {
674          SplitDirectory(pathRest, pathPart, pathRest);
675          PathCat(destination, pathPart);
676       }
677    }
678    len = strlen(destination);
679    if(len>1 && (destination[len-1] == '/' || destination[len-1] == '\\'))
680       destination[--len] = '\0';
681    return destination;
682 }
683
684 public bool StripExtension(char * string)
685 {
686    int c;
687    for(c = strlen(string); c>=0; c--)
688       if(string[c] == '.')
689       {
690          string[c] = '\0';
691          return true;
692       }
693       else if(string[c] == '\\' || string[c] == '/')
694          break;
695    return false;
696 }
697
698 public char * ChangeExtension(const char * string, const char * ext, char * output)
699 {
700    if(string != output)
701       strcpy(output, string);
702    StripExtension(output);
703    if(ext[0])
704       strcat(output, ".");
705    strcat(output, ext);
706    return output;
707 }
708
709 // --- String Stuff (Temporarily outside String class) ---
710 public void PrintSize(char * string, uint size, int prec)
711 {
712    if(size > 1024)
713    {
714       char format[8];
715       sprintf(format, "%%.0%df", prec);
716       if(size > 1024 * 1024 * 1024)
717       {
718          sprintf(string, format, size / (float)(1024 * 1024 * 1024));
719          strcat(string, " GB");
720       }
721       else if(size > 1024 * 1024)
722       {
723          sprintf(string, format, size / (float)(1024 * 1024));
724          strcat(string, " MB");
725       }
726       else
727       {
728          sprintf(string, format, size / (float)1024);
729          strcat(string, " KB");
730       }
731    }
732    else
733       sprintf(string, "%d B", size);
734 }
735
736 public void PrintBigSize(char * string, double size, int prec)
737 {
738    if(size > 1024)
739    {
740       char format[8];
741       sprintf(format, "%%.0%df", prec);
742       if(size > 1024.0 * 1024.0 * 1024.0 * 1024.0)
743       {
744          sprintf(string, format, size / (1024 * 1024 * 1024.0 * 1024.0));
745          strcat(string, " TB");
746       }
747       else if(size > 1024.0 * 1024.0 * 1024.0)
748       {
749          sprintf(string, format, size / (1024.0 * 1024.0 * 1024.0));
750          strcat(string, " GB");
751       }
752       else if(size > 1024.0 * 1024.0)
753       {
754          sprintf(string, format, size / (1024.0 * 1024.0));
755          strcat(string, " MB");
756       }
757       else
758       {
759          sprintf(string, format, size / 1024.0);
760          strcat(string, " KB");
761       }
762    }
763    else
764       sprintf(string, "%.0f B", size);
765 }
766
767 public char * SearchString(const char * buffer, int start, const char * subStr, bool matchCase, bool matchWord)
768 {
769    if(buffer && subStr)
770    {
771       const char * ptr;
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;
776
777       for(ptr = strBuffer; *ptr; ptr++)
778       {
779          if(matchCase ? (*subStr == *ptr) : (tolower(*subStr) == tolower(*ptr)))
780          {
781             if(matchWord)
782             {
783                if(!strcompare(ptr,subStr,subLen) &&
784                   /*
785                   !IS_ALUNDER(ptr[subLen]) &&
786                   !IS_ALUNDER(beforeChar))
787                   */
788                   (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr[subLen])) &&
789                   (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(beforeChar)))
790                   return (char *)ptr;
791             }
792             else
793             {
794                if(!strcompare(ptr,subStr,subLen))
795                   return (char *)ptr;
796             }
797          }
798          beforeChar = ptr[0];
799       }
800    }
801    return null;
802 }
803
804 public char * RSearchString(const char * buffer, const char * subStr, int maxLen, bool matchCase, bool matchWord)
805 {
806    if(buffer && subStr)
807    {
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--)
813       {
814          if(tolower(*subStr) == tolower(*ptr1))
815          {
816             if(matchWord)
817             {
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)))
822
823                  return (char *)ptr1;
824             }
825             else
826             {
827                if(!strcompare(ptr1,subStr,subLen))
828                   return (char *)ptr1;
829             }
830          }
831       }
832    }
833    return null;
834 }
835
836 //public define gnuMakeCharsNeedEscaping = "$%";
837 //public define windowsFileNameCharsNotAllowed = "*/:<>?\\\"|";
838 //public define linuxFileNameCharsNotAllowed = "/";
839 //public define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
840 //public define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
841
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)
845 {
846    const char * escChars, * escCharsQuoted;
847    int count = 0;
848    bool quoted = false, escaped = false;
849    char * start = null, * output = string;
850    char ch;
851    if(__runtimePlatform == win32)
852    {
853 //define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
854       escChars = " !\"%&'()+,;=[]^`{}~"; // windowsFileNameCharsNeedEscaping;
855       escCharsQuoted = "\"";
856    }
857    else
858    {
859 //define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
860       escChars = " !\"$&'()*:;<=>?[\\`{|"; // linuxFileNameCharsNeedEscaping;
861       escCharsQuoted = "\"()$";
862    }
863
864    for(; (ch = *string) && count<maxTokens; string++, output++)
865    {
866       bool wasEscaped = escaped;
867       if(output != string)
868          *output = ch;
869       if(start)
870       {
871          if(escaped)
872          {
873             escaped = false;
874             output--;
875             *output = ch;
876          }
877          else if(ch == '\"')
878          {
879             quoted ^= true;
880             output--;
881          }
882          else if(ch == ' ' && !quoted)
883          {
884             tokens[count++] = start;
885             *output = '\0';
886             start = null;
887          }
888       }
889       else if(ch != ' ')
890       {
891          if(ch == '\"')
892          {
893             quoted = true;
894             start = output+1;
895          }
896          else
897             start = output;
898       }
899       if(!wasEscaped && ch == '\\' && ( esc == true || (esc == forArgsPassing && strchr(quoted ? escCharsQuoted : escChars, *(string+1))) ))
900          escaped = true;
901    }
902    if(start && count < maxTokens)
903    {
904       tokens[count++] = start;
905       *output = '\0';
906    }
907    return count;
908 }
909
910 public int TokenizeWith(char * string, int maxTokens, char* tokens[], const char * tokenizers, bool escapeBackSlashes)
911 {
912    int count = 0;
913    bool quoted = false;
914    char * start = null;
915    bool escaped = false;
916    char * output = string;
917    bool quotedFromStart = false;
918
919    for(; *string && count < maxTokens; string++, output++)
920    {
921       if(output != string)
922          *output = *string;
923       if(start)
924       {
925          if(escaped)
926          {
927             escaped = false;
928             output--;
929             if(output != string)
930                *output = *string;
931          }
932          else if(escapeBackSlashes && *string == '\\')
933             escaped = true;
934          else if(*string == '\"')
935          {
936             if(quoted)
937             {
938                if(quotedFromStart)
939                   *output = '\0';
940                quotedFromStart = false;
941                quoted = false;
942             }
943             else
944                quoted = true;
945          }
946          else if(strchr(tokenizers, *string) && !quoted)
947          {
948             tokens[count++] = start;
949             *output = '\0';
950             start = null;
951          }
952          // MOVED THIS INSIDE IF ABOVE...
953          //if(output != string)
954          //   *output = *string;
955       }
956       else if(!strchr(tokenizers, *string))
957       {
958          if(*string == '\"')
959          {
960             quotedFromStart = true;
961             quoted = true;
962             start = output + 1;
963          }
964          else
965          {
966             start = output;
967             if(*string == '\\' && escapeBackSlashes)
968                escaped = true;
969          }
970       }
971    }
972    if(start && count < maxTokens)
973    {
974       tokens[count++] = start;
975       *output = '\0';
976    }
977    return count;
978 }
979
980 public char * TrimLSpaces(const char * string, char * output)
981 {
982    int c;
983    for(c = 0; string[c] && string[c] == ' '; c++);
984    memmove(output, string + c, strlen(string+c)+1);
985    return output;
986 }
987
988 public char * TrimRSpaces(const char * string, char * output)
989 {
990    int c;
991    for(c = strlen(string)-1; c >= 0 && string[c] == ' '; c--);
992    if(c >= 0)
993    {
994       memmove(output, string, c+1);
995       output[c+1] = '\0';
996    }
997    else
998       output[0] = '\0';
999    return output;
1000 }
1001
1002 public void ChangeCh(char * string, char ch1, char ch2)
1003 {
1004    int c;
1005    for(c=0; string[c]; c++)
1006       if(string[c] == ch1) string[c] = ch2;
1007 }
1008
1009 public void ChangeChars(char * string, const char * chars, char alt)
1010 {
1011    int c;
1012    for(c=0; string[c]; c++)
1013       if(strchr(chars, string[c])) string[c] = alt;
1014 }
1015
1016 public void RepeatCh(char * string, int count, char ch)
1017 {
1018    int c;
1019    for(c=0; c<count; c++)
1020       string[c] = ch;
1021    string[c] = 0;
1022 }
1023
1024 public char * CopyString(const char * string)
1025 {
1026    if(string)
1027    {
1028       int len = strlen(string);
1029       char * destination = new char[len+1];
1030       if(destination)
1031          memcpy(destination, string, len + 1);
1032       return destination;
1033    }
1034    else
1035       return null;
1036 }
1037
1038 public bool GetString(char ** buffer, char * string, int max)
1039 {
1040    int c;
1041    char ch;
1042    bool quoted = false;
1043    bool result = true;
1044    if(!**buffer) { string[0]=0; return false; }
1045
1046    for(;;)
1047    {
1048       if(!(ch = *((*buffer)++)))
1049          result = false;
1050       if( (ch!='\n') && (ch!='\r') && (ch!=' ') && (ch!=',') && (ch!='\t'))
1051          break;
1052       if(!*(*buffer)) break;
1053    }
1054    if(result)
1055    {
1056       for(c=0; c<max-1; c++)
1057       {
1058          if(!quoted && ((ch=='\n')||(ch=='\r')||(ch==' ')||(ch==',')||(ch=='\t')))
1059          {
1060             result = true;
1061             break;
1062          }
1063          if(ch == '\"')
1064          {
1065             quoted ^= 1;
1066             c--;
1067          }
1068          else
1069             string[c]=ch;
1070
1071          if(!(ch = *(*buffer)))
1072          {
1073             c++;
1074             //result = false;
1075             break;
1076          }
1077          (*buffer)++;
1078       }
1079       string[c]=0;
1080    }
1081    return result;
1082 }
1083
1084 public int GetValue(char ** buffer)
1085 {
1086    char string[20];
1087    GetString(buffer,string,20);
1088    return atoi(string);
1089 }
1090
1091 public uint GetHexValue(char ** buffer)
1092 {
1093    char string[20];
1094    GetString(buffer,string,20);
1095    return (uint)strtoul(string, null, 16);
1096 }
1097
1098 public char * StripQuotes(const char * string, char * output)
1099 {
1100    int len;
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';
1106    return output;
1107 }
1108
1109 public double FloatFromString(const char * string)
1110 {
1111    int c, dig;
1112    float dec = 0,res = 0;
1113    int neg = 1;
1114    char ch;
1115    for(c = 0; string[c]; c++)
1116    {
1117       ch = string[c];
1118       if(ch == ' ') continue;
1119       if(ch == '-')
1120       {
1121          if(neg == -1) break;
1122          neg = -1;
1123       }
1124       else if((ch == '.') && !dec)
1125          dec = 10;
1126       else if(isdigit(ch))
1127       {
1128          dig = ch - '0';
1129          if(dec)
1130          {
1131             res += dig / dec;
1132             dec *= 10;
1133          }
1134          else
1135             res = res * 10 + dig;
1136       }
1137       else
1138          break;
1139    }
1140    return neg * res;
1141 }
1142
1143 public bool IsPathInsideOf(const char * path, const char * of)
1144 {
1145    if(!path[0] || !of[0])
1146       return false;  // What to do here? Ever used?
1147    else
1148    {
1149       char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION];
1150       char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION];
1151       strcpy(ofRest, of);
1152       strcpy(pathRest, path);
1153       for(; ofRest[0] && pathRest[0];)
1154       {
1155          SplitDirectory(ofRest, ofPart, ofRest);
1156          SplitDirectory(pathRest, pathPart, pathRest);
1157          if(fstrcmp(pathPart, ofPart))
1158             return false;
1159       }
1160       if(!ofRest[0] && !pathRest[0])  // paths are identical - should return false or true? (changed to false)
1161          return false;
1162       else if(!pathRest[0])           // not inside of, it's the other way around
1163          return false;
1164       return true;
1165    }
1166 }
1167
1168 public enum StringAllocType { pointer, stack, heap };
1169 public class ZString
1170 {
1171 public:
1172    char * _string;
1173    int len;
1174    StringAllocType allocType;
1175    int size;
1176    int minSize;
1177    int maxSize;
1178
1179    ZString()
1180    {
1181       maxSize = MAXINT;
1182    }
1183
1184    ~ZString()
1185    {
1186       if(allocType == heap)
1187          delete _string;
1188    }
1189
1190    void copyString(char * value, int newLen)
1191    {
1192       if(allocType == pointer)
1193       {
1194          size = 0;
1195          _string = null;
1196          allocType = heap;
1197       }
1198       if(allocType == heap)
1199       {
1200          int newSize = newLen ? newLen + 1 : 0;
1201          if(newSize != size)
1202          {
1203             if(newSize < minSize) newSize = minSize;
1204             else if(newSize > maxSize) newSize = maxSize;
1205
1206             if(newSize && size)
1207                _string = renew _string char[newSize];
1208             else if(newSize)
1209                _string = new char[newSize];
1210             else
1211                delete _string;
1212             size = newSize;
1213          }
1214       }
1215       if(newLen + 1 > size) newLen = size-1;
1216       len = newLen;
1217
1218       if(value)
1219       {
1220          memcpy(_string, value, newLen);
1221          _string[newLen] = 0;
1222       }
1223    }
1224
1225 public:
1226
1227    const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
1228    {
1229       return _string;
1230    }
1231
1232    bool OnGetDataFromString(const char * string)
1233    {
1234       property::string = (char *)string;
1235       return true;
1236    }
1237
1238    property char * string
1239    {
1240       set { copyString(value, value ? strlen(value) : 0); }
1241       get { return _string; }
1242    }
1243
1244    property const char *
1245    {
1246       get { return _string; }
1247       set
1248       {
1249          return
1250          {
1251             len = value ? strlen(value) : 0;
1252             _string = (char *)value;
1253             allocType = pointer;
1254          };
1255       }
1256    }
1257
1258    void concatf(const char * format, ...)
1259    {
1260       if(format && allocType != pointer)
1261       {
1262          int addedLen;
1263          va_list args;
1264          va_start(args, format);
1265          if(size < minSize)
1266          {
1267             _string = renew _string char[minSize];
1268             size = minSize;
1269          }
1270          addedLen = vsnprintf(string + len, Max(0, size - 1 - len), format, args);
1271          if(addedLen > 0)
1272          {
1273             len += addedLen;
1274             _string[len] = 0;
1275          }
1276          va_end(args);
1277       }
1278    }
1279
1280    void concat(ZString s)
1281    {
1282       if(s && allocType != pointer)
1283       {
1284          int addedLen = s.len;
1285          int newLen = len + addedLen;
1286          if(allocType == heap && newLen + 1 > size)
1287          {
1288             int newSize = newLen + 1;
1289             if(newSize > maxSize)
1290                newSize = maxSize;
1291             if(newSize > size)
1292             {
1293                _string = renew _string char[newLen];
1294                size = newSize;
1295             }
1296          }
1297          if(newLen + 1 > size)
1298             addedLen = size - 1 - len;
1299          if(addedLen > 0)
1300          {
1301             memcpy(_string + len, s._string, addedLen);
1302             len += addedLen;
1303             _string[len] = 0;
1304          }
1305       }
1306    }
1307
1308    void copy(ZString s)
1309    {
1310       copyString(s._string, s.len);
1311    }
1312 };