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 = (GetRuntimePlatform() == win32) ? '\\' : '/';
34 public define DIR_SEPS = (GetRuntimePlatform() == 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(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(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(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(char * fileName, char * archiveName, 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, char * addedPath)
193 bool modified = false;
196 char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "", * file = null;
199 bool isArchive = SplitArchivePath(string, archiveName, &file);
201 char * protocolSymbol;
203 strcpy(fileName, isArchive ? file : string);
205 if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
207 protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
210 char * slash = strstr(protocolSymbol + 3, "/");
215 urlFileName = fileName + strlen(fileName);
219 protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
222 int len = protocolSymbol - addedPath + 3;
223 memcpy(fileName, addedPath, len);
228 else if(GetRuntimePlatform() == win32)
230 if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
232 fileName[0] = (char)toupper(addedPath[0]);
238 else if(addedPath[0] == '\\' && addedPath[1] == '\\')
240 fileName[0] = fileName[1] = '\\';
245 // A drive needs to be selected
246 /* TOCHECK: Cutting this out, can't handle relative path
247 else if(fileName[0] == '/' && !archiveName[0] && strcmp(addedPath, "/"))
252 if(!modified && isURL && (addedPath[0] == '\\' || addedPath[0] == '/'))
254 urlFileName[0] = '/';
255 urlFileName[1] = '\0';
257 else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
259 if(GetRuntimePlatform() == win32)
262 if(addedPath[0] == '/' && !addedPath[1])
264 fileName[0] = addedPath[0];
269 else if(fileName[0] && fileName[1] == ':')
274 // Relative path root of drive
291 for(; addedPath[c]; )
293 // DANGER OF OVERFLOW HERE
294 // char directory[MAX_FILENAME];
295 char directory[MAX_FILENAME * 16];
300 for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
301 for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
303 if(isURL && ch == '?')
307 if(len < MAX_FILENAME)
308 directory[len++] = ch;
310 directory[len] = '\0';
312 // Trim rightmost spaces
313 for(count = len-1; count >= 0 && (directory[count] == ' ' || directory[count] == '\t'); count--)
315 directory[count] = '\0';
322 if(strstr(directory, "..") == directory && (!directory[2] || directory[2] == DIR_SEP || directory[2] == '/'))
324 int strLen = strlen(fileName) - 1;
327 // Go back one directory
328 for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--);
329 for(;(ch = fileName[strLen]) && strLen > -1 && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
330 for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--);
334 strLen = Max(strLen, urlFileName - fileName);
336 if(!strcmp(fileName + strLen + 1, ".."))
338 strcat(fileName, "/" /*DIR_SEPS*/);
339 strcat(fileName, "..");
343 if(GetRuntimePlatform() == win32)
345 if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
357 fileName[strLen+1] = '\0';
361 fileName[strLen+1] = '\0';
373 strcpy(fileName, "..");
376 else if(strcmp(directory, "."))
378 int strLen = strlen(fileName);
379 bool notZeroLen = strLen > 0;
380 // if(strLen > 1 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
381 if(strLen > 0 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
383 if(notZeroLen /*&& fileName[strLen-1] != ':' && fileName[strLen-1] != '>'*/)
384 fileName[strLen++] = '/';
386 fileName[strLen] = '\0';
388 if(strLen + strlen(directory) > MAX_LOCATION - 3)
389 return null; // AN ERROR OCCURED!
391 strcat(fileName, directory);
394 if(isURL && ch == '/')
395 strcat(fileName, "/");
396 if(isURL && ch == '?')
398 strcat(fileName, addedPath+c);
403 sprintf(string, "<%s>%s", archiveName, fileName);
405 strcpy(string, fileName);
407 return modified ? string : null;
410 public char * PathCat(char * string, char * addedPath)
412 bool modified = false;
415 char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "", * file = null;
418 bool isArchive = SplitArchivePath(string, archiveName, &file);
420 char * protocolSymbol;
422 strcpy(fileName, isArchive ? file : string);
424 if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
426 protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
429 char * slash = strstr(protocolSymbol + 3, "/");
434 urlFileName = fileName + strlen(fileName);
438 protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
441 int len = protocolSymbol - addedPath + 3;
442 memcpy(fileName, addedPath, len);
447 else if(runtimePlatform == win32)
449 if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
451 fileName[0] = (char)toupper(addedPath[0]);
457 else if(addedPath[0] == '\\' && addedPath[1] == '\\')
459 fileName[0] = fileName[1] = '\\';
464 // A drive needs to be selected
465 else if(fileName[0] == '/' && !archiveName[0] && strcmp(addedPath, "/"))
469 if(!modified && isURL && (addedPath[0] == '\\' || addedPath[0] == '/'))
471 urlFileName[0] = '/';
472 urlFileName[1] = '\0';
474 else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
476 if(runtimePlatform == win32)
479 if(addedPath[0] == '/' && !addedPath[1])
481 fileName[0] = addedPath[0];
486 else if(fileName[0] && fileName[1] == ':')
491 // Relative path root of drive
508 for(; addedPath[c]; )
510 // DANGER OF OVERFLOW HERE
511 // char directory[MAX_FILENAME];
512 char directory[MAX_FILENAME * 16];
517 for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
518 for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
520 if(isURL && ch == '?')
524 if(len < MAX_FILENAME)
525 directory[len++] = ch;
527 directory[len] = '\0';
529 // Trim rightmost spaces
530 for(count = len-1; count >= 0 && (directory[count] == ' ' || directory[count] == '\t'); count--)
532 directory[count] = '\0';
539 if(strstr(directory, "..") == directory && (!directory[2] || directory[2] == DIR_SEP))
541 int strLen = strlen(fileName) - 1;
544 bool separator = false;
546 // Go back one directory
547 for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--);
548 for(;(ch = fileName[strLen]) && strLen > -1 && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
549 for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--) separator = true;
553 strLen = Max(strLen, urlFileName - fileName);
555 if(!strcmp(fileName + strLen + (separator ? 2 : 1), ".."))
557 strcat(fileName, DIR_SEPS);
558 strcat(fileName, "..");
562 if(runtimePlatform == win32)
564 if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
576 fileName[strLen+1] = '\0';
580 fileName[strLen+1] = '\0';
592 strcpy(fileName, "..");
595 else if(strcmp(directory, "."))
597 int strLen = strlen(fileName);
598 bool notZeroLen = strLen > 0;
599 // if(strLen > 1 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
600 if(strLen > 0 && (fileName[strLen-1] == '/' || fileName[strLen-1] == '\\'))
602 if(notZeroLen /*&& fileName[strLen-1] != ':' && fileName[strLen-1] != '>'*/)
605 fileName[strLen++] = '/';
607 fileName[strLen++] = DIR_SEP;
610 fileName[strLen] = '\0';
612 if(strLen + strlen(directory) > MAX_LOCATION - 3)
613 return null; // AN ERROR OCCURED!
615 strcat(fileName, directory);
618 if(isURL && ch == '/')
619 strcat(fileName, "/");
620 if(isURL && ch == '?')
622 strcat(fileName, addedPath+c);
627 sprintf(string, "<%s>%s", archiveName, fileName);
629 strcpy(string, fileName);
631 return modified ? string : null;
634 public char * MakePathRelative(char * path, char * to, char * destination)
637 // Don't process empty paths
639 memmove(destination, path, strlen(path)+1);
642 // TOFIX: DANGER OF OVERFLOW HERE
643 char pathPart[MAX_FILENAME * 16], pathRest[MAX_LOCATION];
644 char toPart[MAX_FILENAME * 16], toRest[MAX_LOCATION];
645 bool different = false;
647 strcpy(pathRest, path);
650 destination[0] = '\0';
653 SplitDirectory(toRest, toPart, toRest);
655 SplitDirectory(pathRest, pathPart, pathRest);
657 if(different || fstrcmp(toPart, pathPart))
660 strcat(destination, "..");
661 strcat(destination, DIR_SEPS);
666 PathCat(destination, pathPart);
669 SplitDirectory(pathRest, pathPart, pathRest);
670 PathCat(destination, pathPart);
673 len = strlen(destination);
674 if(len>1 && (destination[len-1] == '/' || destination[len-1] == '\\'))
675 destination[--len] = '\0';
679 public bool StripExtension(char * string)
682 for(c = strlen(string); c>=0; c--)
688 else if(string[c] == '\\' || string[c] == '/')
693 public char * ChangeExtension(char * string, char * ext, char * output)
696 strcpy(output, string);
697 StripExtension(output);
704 // --- String Stuff (Temporarily outside String class) ---
705 public void PrintSize(char * string, uint size, int prec)
710 sprintf(format, "%%.0%df", prec);
711 if(size > 1024 * 1024 * 1024)
713 sprintf(string, format, size / (float)(1024 * 1024 * 1024));
714 strcat(string, " GB");
716 else if(size > 1024 * 1024)
718 sprintf(string, format, size / (float)(1024 * 1024));
719 strcat(string, " MB");
723 sprintf(string, format, size / (float)1024);
724 strcat(string, " KB");
728 sprintf(string, "%d B", size);
731 public void PrintBigSize(char * string, double size, int prec)
736 sprintf(format, "%%.0%df", prec);
737 if(size > 1024.0 * 1024.0 * 1024.0 * 1024.0)
739 sprintf(string, format, size / (1024 * 1024 * 1024.0 * 1024.0));
740 strcat(string, " TB");
742 else if(size > 1024.0 * 1024.0 * 1024.0)
744 sprintf(string, format, size / (1024.0 * 1024.0 * 1024.0));
745 strcat(string, " GB");
747 else if(size > 1024.0 * 1024.0)
749 sprintf(string, format, size / (1024.0 * 1024.0));
750 strcat(string, " MB");
754 sprintf(string, format, size / 1024.0);
755 strcat(string, " KB");
759 sprintf(string, "%.0f B", size);
762 public char * SearchString(char * buffer, int start, char * subStr, bool matchCase, bool matchWord)
767 char * strBuffer = buffer + start;
768 int subLen = strlen(subStr);
769 char beforeChar = start ? *(strBuffer-1) : 0;
770 int (*strcompare)(const char *, const char *, unsigned int) = matchCase ? strncmp : strnicmp;
772 for(ptr = strBuffer; *ptr; ptr++)
774 if(matchCase ? (*subStr == *ptr) : (tolower(*subStr) == tolower(*ptr)))
778 if(!strcompare(ptr,subStr,subLen) &&
780 !IS_ALUNDER(ptr[subLen]) &&
781 !IS_ALUNDER(beforeChar))
783 (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr[subLen])) &&
784 (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(beforeChar)))
789 if(!strcompare(ptr,subStr,subLen))
799 public char * RSearchString(char * buffer, char * subStr, int maxLen, bool matchCase, bool matchWord)
803 int subLen = strlen(subStr);
804 char * ptr1 = buffer + maxLen - subLen;
805 char * ptr2 = buffer + maxLen - subLen - 1;
806 int (*strcompare)(const char *, const char *, unsigned int) = matchCase ? strncmp : strnicmp;
807 for(; ptr1 >=buffer; ptr1--, ptr2--)
809 if(tolower(*subStr) == tolower(*ptr1))
813 if(!strcompare(ptr1,subStr,subLen) &&
814 //!IS_ALUNDER(ptr1[subLen]) && !IS_ALUNDER(*ptr2))
815 (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr1[subLen])) &&
816 (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(*ptr2)))
822 if(!strcompare(ptr1,subStr,subLen))
831 //public define gnuMakeCharsNeedEscaping = "$%";
832 //public define windowsFileNameCharsNotAllowed = "*/:<>?\\\"|";
833 //public define linuxFileNameCharsNotAllowed = "/";
834 //public define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
835 //public define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
837 // fix #139 to remove " = 2" and warnings for backward compatible calls to Tokenize using 'true' for the 'esc' argument;
838 public enum BackSlashEscaping : bool { forArgsPassing = 2 };
839 public int Tokenize(char * string, int maxTokens, char* tokens[], BackSlashEscaping esc)
842 //define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
843 const char * escChars = " !\"%&'()+,;=[]^`{}~"; // windowsFileNameCharsNeedEscaping;
844 const char * escCharsQuoted = "\"";
846 //define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
847 const char * escChars = " !\"$&'()*:;<=>?[\\`{|"; // linuxFileNameCharsNeedEscaping;
848 const char * escCharsQuoted = "\"()$";
851 bool quoted = false, escaped = false;
852 char * start = null, * output = string;
854 for(; (ch = *string) && count<maxTokens; string++, output++)
856 bool wasEscaped = escaped;
872 else if(ch == ' ' && !quoted)
874 tokens[count++] = start;
889 if(!wasEscaped && ch == '\\' && ( esc == true || (esc == forArgsPassing && strchr(quoted ? escCharsQuoted : escChars, *(string+1))) ))
892 if(start && count < maxTokens)
894 tokens[count++] = start;
900 public int TokenizeWith(char * string, int maxTokens, char* tokens[], char * tokenizers, bool escapeBackSlashes)
905 bool escaped = false;
906 char * output = string;
907 bool quotedFromStart = false;
909 for(; *string && count < maxTokens; string++, output++)
922 else if(escapeBackSlashes && *string == '\\')
924 else if(*string == '\"')
930 quotedFromStart = false;
936 else if(strchr(tokenizers, *string) && !quoted)
938 tokens[count++] = start;
942 // MOVED THIS INSIDE IF ABOVE...
943 //if(output != string)
944 // *output = *string;
946 else if(!strchr(tokenizers, *string))
950 quotedFromStart = true;
957 if(*string == '\\' && escapeBackSlashes)
962 if(start && count < maxTokens)
964 tokens[count++] = start;
970 public char * TrimLSpaces(char * string, char * output)
973 for(c = 0; string[c] && string[c] == ' '; c++);
974 memmove(output, string + c, strlen(string+c)+1);
978 public char * TrimRSpaces(char * string, char * output)
981 for(c = strlen(string)-1; c >= 0 && string[c] == ' '; c--);
984 memmove(output, string, c+1);
992 public void ChangeCh(char * string, char ch1, char ch2)
995 for(c=0; string[c]; c++)
996 if(string[c] == ch1) string[c] = ch2;
999 public void RepeatCh(char * string, int count, char ch)
1002 for(c=0; c<count; c++)
1007 public char * CopyString(char * string)
1011 int len = strlen(string);
1012 char * destination = new char[len+1];
1014 memcpy(destination, string, len + 1);
1021 public bool GetString(char ** buffer, char * string, int max)
1025 bool quoted = false;
1027 if(!**buffer) { string[0]=0; return false; }
1031 if(!(ch = *((*buffer)++)))
1033 if( (ch!='\n') && (ch!='\r') && (ch!=' ') && (ch!=',') && (ch!='\t'))
1035 if(!*(*buffer)) break;
1039 for(c=0; c<max-1; c++)
1041 if(!quoted && ((ch=='\n')||(ch=='\r')||(ch==' ')||(ch==',')||(ch=='\t')))
1054 if(!(ch = *(*buffer)))
1067 public int GetValue(char ** buffer)
1070 GetString(buffer,string,20);
1071 return atoi(string);
1074 public uint GetHexValue(char ** buffer)
1077 GetString(buffer,string,20);
1078 return (uint)strtoul(string, null, 16);
1081 public char * StripQuotes(char * string, char * output)
1084 char * src = (string[0] == '\"') ? (string+1) : string;
1085 memmove(output, src, strlen(src)+1);
1086 len = strlen(output);
1087 if(len && output[len-1] == '\"')
1088 output[len-1] = '\0';
1092 public double FloatFromString(char * string)
1095 float dec = 0,res = 0;
1098 for(c = 0; string[c]; c++)
1101 if(ch == ' ') continue;
1104 if(neg == -1) break;
1107 else if((ch == '.') && !dec)
1109 else if(isdigit(ch))
1118 res = res * 10 + dig;
1126 public bool IsPathInsideOf(char * path, char * of)
1128 if(!path[0] || !of[0])
1129 return false; // What to do here? Ever used?
1132 char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION];
1133 char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION];
1135 strcpy(pathRest, path);
1136 for(; ofRest[0] && pathRest[0];)
1138 SplitDirectory(ofRest, ofPart, ofRest);
1139 SplitDirectory(pathRest, pathPart, pathRest);
1140 if(fstrcmp(pathPart, ofPart))
1143 if(!ofRest[0] && !pathRest[0]) // paths are identical - should return false or true? (changed to false)
1145 else if(!pathRest[0]) // not inside of, it's the other way around