11 #if !defined(ECERE_BOOTSTRAP) // quick fix for now
12 #if defined(__WIN32__)
13 #define WIN32_LEAN_AND_MEAN
14 #define String _String
29 default extern Platform runtimePlatform;
31 #define IS_ALUNDER(ch) ((ch) == '_' || isalnum((ch)))
33 public define DIR_SEP = (__runtimePlatform == win32) ? '\\' : '/';
34 public define DIR_SEPS = (__runtimePlatform == win32) ? "\\" : "/";
36 // Maximum length for a vsnprintf string
37 public define MAX_F_STRING = 1025;
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
45 // --- File related String functions ---
46 public char * GetExtension(const char * string, char * output)
49 int len = strlen(string);
50 int limit = Max(0, len-MAX_EXTENSION);
52 for(c = len; c>=limit; c--)
57 strcpy(output, string+c+1);
60 else if(ch == '/' || ch == '\\')
66 public char * StripLastDirectory(const char * string, char * output)
69 if(runtimePlatform == win32 && !strcmp(string, "\\\\"))
76 int len = strlen(string);
77 for(c = len-2; c>=0; c--)
78 if(string[c] == '/' || string[c] == '\\')
80 else if(string[c] == '>' || (string[c] == ':' && c == 0))
86 if((runtimePlatform == win32) ? (c >= 0) : (c > 0))
88 memmove(output, string, c);
91 if(runtimePlatform == win32 && c == 1 && output[0] == '\\' && output[1] == '\\')
97 strcpy(output, DIR_SEPS);
102 // Return root on UNIX instead of null...
105 strcpy(output, DIR_SEPS);
117 public char * SplitDirectory(const char * string, char * part, char * rest)
122 for(;(ch = string[c]) && (ch == '/' || ch == '\\'); c++);
125 part[len++] = DIR_SEP;
128 for(;(ch = string[c]) && (ch != '/' && ch != '\\'); c++)
130 if(len < MAX_FILENAME)
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 != '\\')
147 public char * GetLastDirectory(const char * string, char * output)
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] == '>')
157 memmove(output, string+c, strlen(string+c)+1);
161 len = strlen(output);
162 if(len > 1 && (output[len-1] == '\\' || output[len-1] == '/'))
163 output[len-1] = '\0';
167 public bool SplitArchivePath(const char * fileName, char * archiveName, const char * * archiveFile)
170 if(fileName[0] == '<')
172 int c = strlen(fileName);
173 for(;c>0 && fileName[c] != '>'; c--);
176 strncpy(archiveName, fileName + 1, c - 1);
177 archiveName[c - 1] = '\0';
178 *archiveFile = fileName + c + 1;
182 else if(fileName[0] == ':')
184 strcpy(archiveName, ":");
185 *archiveFile = fileName + 1;
191 public char * PathCatSlash(char * string, const char * addedPath)
193 bool modified = false;
196 char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "";
197 const char * file = null;
200 bool isArchive = SplitArchivePath(string, archiveName, &file);
202 char * protocolSymbol;
204 strcpy(fileName, isArchive ? file : string);
206 if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
208 protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
211 char * slash = strstr(protocolSymbol + 3, "/");
216 urlFileName = fileName + strlen(fileName);
220 protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
223 int len = protocolSymbol - addedPath + 3;
224 memcpy(fileName, addedPath, len);
229 else if(__runtimePlatform == win32)
231 if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
233 fileName[0] = (char)toupper(addedPath[0]);
239 else if(addedPath[0] == '\\' && addedPath[1] == '\\')
241 fileName[0] = fileName[1] = '\\';
246 // A drive needs to be selected
247 /* TOCHECK: Cutting this out, can't handle relative path
248 else if(fileName[0] == '/' && !archiveName[0] && strcmp(addedPath, "/"))
253 if(!modified && isURL && (addedPath[0] == '\\' || addedPath[0] == '/'))
255 urlFileName[0] = '/';
256 urlFileName[1] = '\0';
258 else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
260 if(__runtimePlatform == win32)
263 if(addedPath[0] == '/' && !addedPath[1])
265 fileName[0] = addedPath[0];
270 else if(fileName[0] && fileName[1] == ':')
275 // Relative path root of drive
292 for(; addedPath[c]; )
294 // DANGER OF OVERFLOW HERE
295 // char directory[MAX_FILENAME];
296 char directory[MAX_FILENAME * 16];
301 for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
302 for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
304 if(isURL && ch == '?')
308 if(len < MAX_FILENAME)
309 directory[len++] = ch;
311 directory[len] = '\0';
313 // Trim rightmost spaces
314 for(count = len-1; count >= 0 && (directory[count] == ' ' || directory[count] == '\t'); count--)
316 directory[count] = '\0';
323 if(strstr(directory, "..") == directory && (!directory[2] || directory[2] == DIR_SEP || directory[2] == '/'))
325 int strLen = strlen(fileName) - 1;
328 // Go back one directory
329 for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--);
330 for(;strLen > -1 && (ch = fileName[strLen]) && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
331 for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--);
335 strLen = Max(strLen, urlFileName - fileName);
337 if(!strcmp(fileName + strLen + 1, ".."))
339 strcat(fileName, "/" /*DIR_SEPS*/);
340 strcat(fileName, "..");
344 if(__runtimePlatform == win32)
346 if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
358 fileName[strLen+1] = '\0';
362 fileName[strLen+1] = '\0';
374 strcpy(fileName, "..");
377 else if(strcmp(directory, "."))
379 int strLen = strlen(fileName);
380 bool notZeroLen = strLen > 0;
381 // if(strLen > 1 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
382 if(strLen > 0 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
384 if(notZeroLen /*&& fileName[strLen-1] != ':' && fileName[strLen-1] != '>'*/)
385 fileName[strLen++] = '/';
387 fileName[strLen] = '\0';
389 if(strLen + strlen(directory) > MAX_LOCATION - 3)
390 return null; // AN ERROR OCCURED!
392 strcat(fileName, directory);
395 if(isURL && ch == '/')
396 strcat(fileName, "/");
397 if(isURL && ch == '?')
399 strcat(fileName, addedPath+c);
404 sprintf(string, "<%s>%s", archiveName, fileName);
406 strcpy(string, fileName);
408 return modified ? string : null;
411 public char * PathCat(char * string, const char * addedPath)
413 bool modified = false;
416 char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "";
417 const char * file = null;
420 bool isArchive = SplitArchivePath(string, archiveName, &file);
422 char * protocolSymbol;
424 strcpy(fileName, isArchive ? file : string);
426 if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
428 protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
431 char * slash = strstr(protocolSymbol + 3, "/");
436 urlFileName = fileName + strlen(fileName);
440 protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
443 int len = protocolSymbol - addedPath + 3;
444 memcpy(fileName, addedPath, len);
449 else if(runtimePlatform == win32)
451 if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
453 fileName[0] = (char)toupper(addedPath[0]);
459 else if(addedPath[0] == '\\' && addedPath[1] == '\\')
461 fileName[0] = fileName[1] = '\\';
466 // A drive needs to be selected
467 else if(fileName[0] == '/' && !archiveName[0] && strcmp(addedPath, "/"))
471 if(!modified && isURL && (addedPath[0] == '\\' || addedPath[0] == '/'))
473 urlFileName[0] = '/';
474 urlFileName[1] = '\0';
476 else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
478 if(runtimePlatform == win32)
481 if(addedPath[0] == '/' && !addedPath[1])
483 fileName[0] = addedPath[0];
488 else if(fileName[0] && fileName[1] == ':')
493 // Relative path root of drive
510 for(; addedPath[c]; )
512 // DANGER OF OVERFLOW HERE
513 // char directory[MAX_FILENAME];
514 char directory[MAX_FILENAME * 16];
519 for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
520 for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
522 if(isURL && ch == '?')
526 if(len < MAX_FILENAME)
527 directory[len++] = ch;
529 directory[len] = '\0';
531 // Trim rightmost spaces
532 for(count = len-1; count >= 0 && (directory[count] == ' ' || directory[count] == '\t'); count--)
534 directory[count] = '\0';
541 if(strstr(directory, "..") == directory && (!directory[2] || directory[2] == DIR_SEP))
543 int strLen = strlen(fileName) - 1;
546 bool separator = false;
548 // Go back one directory
549 for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--);
550 for(;strLen > -1 && (ch = fileName[strLen]) && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
551 for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--) separator = true;
555 strLen = Max(strLen, urlFileName - fileName);
557 if(!strcmp(fileName + strLen + (separator ? 2 : 1), ".."))
559 strcat(fileName, DIR_SEPS);
560 strcat(fileName, "..");
564 if(runtimePlatform == win32)
566 if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
578 fileName[strLen+1] = '\0';
582 fileName[strLen+1] = '\0';
594 strcpy(fileName, "..");
597 else if(strcmp(directory, "."))
599 int strLen = strlen(fileName);
600 bool notZeroLen = strLen > 0;
601 // if(strLen > 1 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
602 if(strLen > 0 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
604 if(notZeroLen /*&& fileName[strLen-1] != ':' && fileName[strLen-1] != '>'*/)
607 fileName[strLen++] = '/';
609 fileName[strLen++] = DIR_SEP;
612 fileName[strLen] = '\0';
614 if(strLen + strlen(directory) > MAX_LOCATION - 3)
615 return null; // AN ERROR OCCURED!
617 strcat(fileName, directory);
620 if(isURL && ch == '/')
621 strcat(fileName, "/");
622 if(isURL && ch == '?')
624 strcat(fileName, addedPath+c);
629 sprintf(string, "<%s>%s", archiveName, fileName);
631 strcpy(string, fileName);
633 return modified ? string : null;
636 public char * MakePathRelative(const char * path, const char * to, char * destination)
639 // Don't process empty paths
641 memmove(destination, path, strlen(path)+1);
644 // TOFIX: DANGER OF OVERFLOW HERE
645 char pathPart[MAX_FILENAME * 16], pathRest[MAX_LOCATION];
646 char toPart[MAX_FILENAME * 16], toRest[MAX_LOCATION];
647 bool different = false;
649 strcpy(pathRest, path);
652 destination[0] = '\0';
655 SplitDirectory(toRest, toPart, toRest);
657 SplitDirectory(pathRest, pathPart, pathRest);
659 if(different || fstrcmp(toPart, pathPart))
662 strcat(destination, "..");
663 strcat(destination, DIR_SEPS);
668 PathCat(destination, pathPart);
671 SplitDirectory(pathRest, pathPart, pathRest);
672 PathCat(destination, pathPart);
675 len = strlen(destination);
676 if(len>1 && (destination[len-1] == '/' || destination[len-1] == '\\'))
677 destination[--len] = '\0';
681 public bool StripExtension(char * string)
684 for(c = strlen(string); c>=0; c--)
690 else if(string[c] == '\\' || string[c] == '/')
695 public char * ChangeExtension(const char * string, const char * ext, char * output)
698 strcpy(output, string);
699 StripExtension(output);
706 // --- String Stuff (Temporarily outside String class) ---
707 public void PrintSize(char * string, uint size, int prec)
712 sprintf(format, "%%.0%df", prec);
713 if(size > 1024 * 1024 * 1024)
715 sprintf(string, format, size / (float)(1024 * 1024 * 1024));
716 strcat(string, " GB");
718 else if(size > 1024 * 1024)
720 sprintf(string, format, size / (float)(1024 * 1024));
721 strcat(string, " MB");
725 sprintf(string, format, size / (float)1024);
726 strcat(string, " KB");
730 sprintf(string, "%d B", size);
733 public void PrintBigSize(char * string, double size, int prec)
738 sprintf(format, "%%.0%df", prec);
739 if(size > 1024.0 * 1024.0 * 1024.0 * 1024.0)
741 sprintf(string, format, size / (1024 * 1024 * 1024.0 * 1024.0));
742 strcat(string, " TB");
744 else if(size > 1024.0 * 1024.0 * 1024.0)
746 sprintf(string, format, size / (1024.0 * 1024.0 * 1024.0));
747 strcat(string, " GB");
749 else if(size > 1024.0 * 1024.0)
751 sprintf(string, format, size / (1024.0 * 1024.0));
752 strcat(string, " MB");
756 sprintf(string, format, size / 1024.0);
757 strcat(string, " KB");
761 sprintf(string, "%.0f B", size);
764 public char * SearchString(const char * buffer, int start, const char * subStr, bool matchCase, bool matchWord)
769 const char * strBuffer = buffer + start;
770 int subLen = strlen(subStr);
771 char beforeChar = start ? *(strBuffer-1) : 0;
772 int (*strcompare)(const char *, const char *, unsigned int) = matchCase ? strncmp : strnicmp;
774 for(ptr = strBuffer; *ptr; ptr++)
776 if(matchCase ? (*subStr == *ptr) : (tolower(*subStr) == tolower(*ptr)))
780 if(!strcompare(ptr,subStr,subLen) &&
782 !IS_ALUNDER(ptr[subLen]) &&
783 !IS_ALUNDER(beforeChar))
785 (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr[subLen])) &&
786 (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(beforeChar)))
791 if(!strcompare(ptr,subStr,subLen))
801 public char * RSearchString(const char * buffer, const char * subStr, int maxLen, bool matchCase, bool matchWord)
805 int subLen = strlen(subStr);
806 const char * ptr1 = buffer + maxLen - subLen;
807 const char * ptr2 = buffer + maxLen - subLen - 1;
808 int (*strcompare)(const char *, const char *, unsigned int) = matchCase ? strncmp : strnicmp;
809 for(; ptr1 >=buffer; ptr1--, ptr2--)
811 if(tolower(*subStr) == tolower(*ptr1))
815 if(!strcompare(ptr1,subStr,subLen) &&
816 //!IS_ALUNDER(ptr1[subLen]) && !IS_ALUNDER(*ptr2))
817 (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr1[subLen])) &&
818 (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(*ptr2)))
824 if(!strcompare(ptr1,subStr,subLen))
833 //public define gnuMakeCharsNeedEscaping = "$%";
834 //public define windowsFileNameCharsNotAllowed = "*/:<>?\\\"|";
835 //public define linuxFileNameCharsNotAllowed = "/";
836 //public define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
837 //public define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
839 // fix #139 to remove " = 2" and warnings for backward compatible calls to Tokenize using 'true' for the 'esc' argument;
840 public enum BackSlashEscaping : bool { forArgsPassing = 2 };
841 public int Tokenize(char * string, int maxTokens, char* tokens[], BackSlashEscaping esc)
844 //define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
845 const char * escChars = " !\"%&'()+,;=[]^`{}~"; // windowsFileNameCharsNeedEscaping;
846 const char * escCharsQuoted = "\"";
848 //define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
849 const char * escChars = " !\"$&'()*:;<=>?[\\`{|"; // linuxFileNameCharsNeedEscaping;
850 const char * escCharsQuoted = "\"()$";
853 bool quoted = false, escaped = false;
854 char * start = null, * output = string;
856 for(; (ch = *string) && count<maxTokens; string++, output++)
858 bool wasEscaped = escaped;
874 else if(ch == ' ' && !quoted)
876 tokens[count++] = start;
891 if(!wasEscaped && ch == '\\' && ( esc == true || (esc == forArgsPassing && strchr(quoted ? escCharsQuoted : escChars, *(string+1))) ))
894 if(start && count < maxTokens)
896 tokens[count++] = start;
902 public int TokenizeWith(char * string, int maxTokens, char* tokens[], const char * tokenizers, bool escapeBackSlashes)
907 bool escaped = false;
908 char * output = string;
909 bool quotedFromStart = false;
911 for(; *string && count < maxTokens; string++, output++)
924 else if(escapeBackSlashes && *string == '\\')
926 else if(*string == '\"')
932 quotedFromStart = false;
938 else if(strchr(tokenizers, *string) && !quoted)
940 tokens[count++] = start;
944 // MOVED THIS INSIDE IF ABOVE...
945 //if(output != string)
946 // *output = *string;
948 else if(!strchr(tokenizers, *string))
952 quotedFromStart = true;
959 if(*string == '\\' && escapeBackSlashes)
964 if(start && count < maxTokens)
966 tokens[count++] = start;
972 public char * TrimLSpaces(const char * string, char * output)
975 for(c = 0; string[c] && string[c] == ' '; c++);
976 memmove(output, string + c, strlen(string+c)+1);
980 public char * TrimRSpaces(const char * string, char * output)
983 for(c = strlen(string)-1; c >= 0 && string[c] == ' '; c--);
986 memmove(output, string, c+1);
994 public void ChangeCh(char * string, char ch1, char ch2)
997 for(c=0; string[c]; c++)
998 if(string[c] == ch1) string[c] = ch2;
1001 public void RepeatCh(char * string, int count, char ch)
1004 for(c=0; c<count; c++)
1009 public char * CopyString(const char * string)
1013 int len = strlen(string);
1014 char * destination = new char[len+1];
1016 memcpy(destination, string, len + 1);
1023 public bool GetString(char ** buffer, char * string, int max)
1027 bool quoted = false;
1029 if(!**buffer) { string[0]=0; return false; }
1033 if(!(ch = *((*buffer)++)))
1035 if( (ch!='\n') && (ch!='\r') && (ch!=' ') && (ch!=',') && (ch!='\t'))
1037 if(!*(*buffer)) break;
1041 for(c=0; c<max-1; c++)
1043 if(!quoted && ((ch=='\n')||(ch=='\r')||(ch==' ')||(ch==',')||(ch=='\t')))
1056 if(!(ch = *(*buffer)))
1069 public int GetValue(char ** buffer)
1072 GetString(buffer,string,20);
1073 return atoi(string);
1076 public uint GetHexValue(char ** buffer)
1079 GetString(buffer,string,20);
1080 return (uint)strtoul(string, null, 16);
1083 public char * StripQuotes(const char * string, char * output)
1086 const char * src = (string[0] == '\"') ? (string+1) : string;
1087 memmove(output, src, strlen(src)+1);
1088 len = strlen(output);
1089 if(len && output[len-1] == '\"')
1090 output[len-1] = '\0';
1094 public double FloatFromString(const char * string)
1097 float dec = 0,res = 0;
1100 for(c = 0; string[c]; c++)
1103 if(ch == ' ') continue;
1106 if(neg == -1) break;
1109 else if((ch == '.') && !dec)
1111 else if(isdigit(ch))
1120 res = res * 10 + dig;
1128 public bool IsPathInsideOf(const char * path, const char * of)
1130 if(!path[0] || !of[0])
1131 return false; // What to do here? Ever used?
1134 char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION];
1135 char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION];
1137 strcpy(pathRest, path);
1138 for(; ofRest[0] && pathRest[0];)
1140 SplitDirectory(ofRest, ofPart, ofRest);
1141 SplitDirectory(pathRest, pathPart, pathRest);
1142 if(fstrcmp(pathPart, ofPart))
1145 if(!ofRest[0] && !pathRest[0]) // paths are identical - should return false or true? (changed to false)
1147 else if(!pathRest[0]) // not inside of, it's the other way around