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