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