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