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