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