ecere: Added ChangeChars(); eda/ers: ; Avoiding bad filenames
[sdk] / ecere / src / com / String.ec
index 17fa08a..d7b7258 100644 (file)
@@ -30,8 +30,8 @@ default extern Platform runtimePlatform;
 
 #define IS_ALUNDER(ch) ((ch) == '_' || isalnum((ch)))
 
-public define DIR_SEP   = (GetRuntimePlatform() == win32) ? '\\' : '/';
-public define DIR_SEPS  = (GetRuntimePlatform() == win32) ? "\\" : "/";
+public define DIR_SEP   = (__runtimePlatform == win32) ? '\\' : '/';
+public define DIR_SEPS  = (__runtimePlatform == win32) ? "\\" : "/";
 
 // Maximum length for a vsnprintf string
 public define MAX_F_STRING = 1025;
@@ -43,7 +43,7 @@ public define MAX_DIRECTORY = 534; // 8 levels + 8 separators + \0
 public define MAX_LOCATION = 797; // directory + filename + separator + \0
 
 // --- File related String functions ---
-public char * GetExtension(char * string, char * output)
+public char * GetExtension(const char * string, char * output)
 {
    int c;
    int len = strlen(string);
@@ -63,7 +63,7 @@ public char * GetExtension(char * string, char * output)
    return output;
 }
 
-public char * StripLastDirectory(char * string, char * output)
+public char * StripLastDirectory(const char * string, char * output)
 {
    int c;
    if(runtimePlatform == win32 && !strcmp(string, "\\\\"))
@@ -128,14 +128,14 @@ public char * SplitDirectory(const char * string, char * part, char * rest)
       for(;(ch = string[c]) && (ch != '/' && ch != '\\'); c++)
       {
          if(len < MAX_FILENAME)
-            part[len++] = ch;  
+            part[len++] = ch;
       }
    }
 
    for(;(ch = string[c]) && (ch == '/' || ch == '\\'); c++);
    memmove(rest, string + c, strlen(string + c) + 1);
    for(c = strlen(rest); c >= 0; c--)
-      if(ch != '/' && ch != '\\') 
+      if(ch != '/' && ch != '\\')
          break;
    if(c > 0)
       rest[c] = '\0';
@@ -144,7 +144,7 @@ public char * SplitDirectory(const char * string, char * part, char * rest)
    return rest;
 }
 
-public char * GetLastDirectory(char * string, char * output)
+public char * GetLastDirectory(const char * string, char * output)
 {
    int c;
    int len = string ? strlen(string) : 0;
@@ -160,11 +160,11 @@ public char * GetLastDirectory(char * string, char * output)
 
    len = strlen(output);
    if(len > 1 && (output[len-1] == '\\' || output[len-1] == '/'))
-      output[len-1] = '\0';   
+      output[len-1] = '\0';
    return output;
 }
 
-public bool SplitArchivePath(char * fileName, char * archiveName, char ** archiveFile)
+public bool SplitArchivePath(const char * fileName, char * archiveName, const char * * archiveFile)
 {
    // Support Archives
    if(fileName[0] == '<')
@@ -188,39 +188,45 @@ public bool SplitArchivePath(char * fileName, char * archiveName, char ** archiv
    return false;
 }
 
-public char * PathCatSlash(char * string, char * addedPath)
+public char * PathCatSlash(char * string, const char * addedPath)
 {
    bool modified = false;
    if(addedPath)
    {
-      char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "", * file;
+      char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "";
+      const char * file = null;
       int c = 0;
       bool isURL = false;
-      char * urlFileName;
+      bool isArchive = SplitArchivePath(string, archiveName, &file);
+      char * urlFileName = null;
+      char * protocolSymbol;
 
-      if(SplitArchivePath(string, archiveName, &file))
-         strcpy(fileName, file);
-      else
-      {
-         strcpy(fileName, string);
-      }
+      strcpy(fileName, isArchive ? file : string);
 
-      if(strstr(string, "http://") == string)
+      if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
       {
-         char * slash = strstr(fileName + 7, "/");
-         isURL = true;
-         if(slash)
-            urlFileName = slash;
-         else
-            urlFileName = fileName + strlen(fileName);
+         protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
+         if(protocolSymbol)
+         {
+            char * slash = strstr(protocolSymbol + 3, "/");
+            isURL = true;
+            if(slash)
+               urlFileName = slash;
+            else
+               urlFileName = fileName + strlen(fileName);
+         }
       }
-      if(strstr(addedPath, "http://") == addedPath)
+
+      protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
+      if(protocolSymbol)
       {
-         strcpy(fileName, "http://");
+         int len = protocolSymbol - addedPath + 3;
+         memcpy(fileName, addedPath, len);
+         fileName[len] = 0;
          isURL = true;
-         c = 7;
+         c = len;
       }
-      else if(GetRuntimePlatform() == win32)
+      else if(__runtimePlatform == win32)
       {
          if(addedPath[0] && addedPath[1] == ':' && addedPath[0] != '<')
          {
@@ -251,7 +257,7 @@ public char * PathCatSlash(char * string, char * addedPath)
       }
       else if(!modified && (addedPath[0] == '\\' || addedPath[0] == '/'))
       {
-         if(GetRuntimePlatform() == win32)
+         if(__runtimePlatform == win32)
          {
             // Entire Computer
             if(addedPath[0] == '/' && !addedPath[1])
@@ -291,7 +297,7 @@ public char * PathCatSlash(char * string, char * addedPath)
          int len = 0;
          char ch;
          int count;
-      
+
          for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
          for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
          {
@@ -300,7 +306,7 @@ public char * PathCatSlash(char * string, char * addedPath)
                break;
             }
             if(len < MAX_FILENAME)
-               directory[len++] = ch;  
+               directory[len++] = ch;
          }
          directory[len] = '\0';
 
@@ -320,9 +326,9 @@ public char * PathCatSlash(char * string, char * addedPath)
                if(strLen > -1)
                {
                   // Go back one directory
-                  for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--);
-                  for(;(ch = fileName[strLen]) && strLen > -1 && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
-                  for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--);
+                  for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--);
+                  for(;strLen > -1 && (ch = fileName[strLen]) && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
+                  for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--);
 
                   if(isURL)
                   {
@@ -335,7 +341,7 @@ public char * PathCatSlash(char * string, char * addedPath)
                   }
                   else
                   {
-                     if(GetRuntimePlatform() == win32)
+                     if(__runtimePlatform == win32)
                      {
                         if(!strLen && fileName[0] == '\\' && fileName[1] == '\\')
                         {
@@ -402,37 +408,43 @@ public char * PathCatSlash(char * string, char * addedPath)
    return modified ? string : null;
 }
 
-public char * PathCat(char * string, char * addedPath)
+public char * PathCat(char * string, const char * addedPath)
 {
    bool modified = false;
    if(addedPath)
    {
-      char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "", * file;
+      char fileName[MAX_LOCATION] = "", archiveName[MAX_LOCATION] = "";
+      const char * file = null;
       int c = 0;
       bool isURL = false;
-      char * urlFileName;
+      bool isArchive = SplitArchivePath(string, archiveName, &file);
+      char * urlFileName = null;
+      char * protocolSymbol;
 
-      if(SplitArchivePath(string, archiveName, &file))
-         strcpy(fileName, file);
-      else
-      {
-         strcpy(fileName, string);
-      }
+      strcpy(fileName, isArchive ? file : string);
 
-      if(strstr(string, "http://") == string)
+      if(!isArchive) // TODO: Support for PathCat'ing .. outside of archive
       {
-         char * slash = strstr(fileName + 7, "/");
-         isURL = true;
-         if(slash)
-            urlFileName = slash;
-         else
-            urlFileName = fileName + strlen(fileName);
+         protocolSymbol = (fileName[0] && fileName[0] != '.' && fileName[0] != '/' && fileName[0] != '\\' && fileName[1] != ':') ? strstr(fileName, "://") : null;
+         if(protocolSymbol)
+         {
+            char * slash = strstr(protocolSymbol + 3, "/");
+            isURL = true;
+            if(slash)
+               urlFileName = slash;
+            else
+               urlFileName = fileName + strlen(fileName);
+         }
       }
-      if(strstr(addedPath, "http://") == addedPath)
+
+      protocolSymbol = (addedPath[0] && addedPath[0] != '.' && addedPath[0] != '/' && addedPath[0] != '\\' && addedPath[1] != ':') ? strstr(addedPath, "://") : null;
+      if(protocolSymbol)
       {
-         strcpy(fileName, "http://");
+         int len = protocolSymbol - addedPath + 3;
+         memcpy(fileName, addedPath, len);
+         fileName[len] = 0;
          isURL = true;
-         c = 7;
+         c = len;
       }
       else if(runtimePlatform == win32)
       {
@@ -503,7 +515,7 @@ public char * PathCat(char * string, char * addedPath)
          int len = 0;
          char ch;
          int count;
-      
+
          for(;(ch = addedPath[c]) && (ch == '/' || ch == '\\'); c++);
          for(;(ch = addedPath[c]) && (ch != '/' && ch != '\\'); c++)
          {
@@ -512,7 +524,7 @@ public char * PathCat(char * string, char * addedPath)
                break;
             }
             if(len < MAX_FILENAME)
-               directory[len++] = ch;  
+               directory[len++] = ch;
          }
          directory[len] = '\0';
 
@@ -534,9 +546,9 @@ public char * PathCat(char * string, char * addedPath)
                   bool separator = false;
 
                   // Go back one directory
-                  for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--);
-                  for(;(ch = fileName[strLen]) && strLen > -1 && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
-                  for(;(ch = fileName[strLen]) && strLen > -1 && (ch == '/' || ch == '\\'); strLen--) separator = true;
+                  for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--);
+                  for(;strLen > -1 && (ch = fileName[strLen]) && (ch != '/' && ch != '\\' && ch != ':'); strLen--);
+                  for(;strLen > -1 && (ch = fileName[strLen]) && (ch == '/' || ch == '\\'); strLen--) separator = true;
 
                   if(isURL)
                   {
@@ -621,8 +633,9 @@ public char * PathCat(char * string, char * addedPath)
    return modified ? string : null;
 }
 
-public char * MakePathRelative(char * path, char * to, char * destination)
+public char * MakePathRelative(const char * path, const char * to, char * destination)
 {
+   int len;
    // Don't process empty paths
    if(!path[0])
       memmove(destination, path, strlen(path)+1);
@@ -639,7 +652,7 @@ public char * MakePathRelative(char * path, char * to, char * destination)
       destination[0] = '\0';
       for(;toRest[0];)
       {
-         SplitDirectory(toRest, toPart, toRest);      
+         SplitDirectory(toRest, toPart, toRest);
          if(!different)
             SplitDirectory(pathRest, pathPart, pathRest);
 
@@ -659,6 +672,9 @@ public char * MakePathRelative(char * path, char * to, char * destination)
          PathCat(destination, pathPart);
       }
    }
+   len = strlen(destination);
+   if(len>1 && (destination[len-1] == '/' || destination[len-1] == '\\'))
+      destination[--len] = '\0';
    return destination;
 }
 
@@ -676,7 +692,7 @@ public bool StripExtension(char * string)
    return false;
 }
 
-public char * ChangeExtension(char * string, char * ext, char * output)
+public char * ChangeExtension(const char * string, const char * ext, char * output)
 {
    if(string != output)
       strcpy(output, string);
@@ -745,15 +761,15 @@ public void PrintBigSize(char * string, double size, int prec)
       sprintf(string, "%.0f B", size);
 }
 
-public char * SearchString(char * buffer, int start, char * subStr, bool matchCase, bool matchWord)
+public char * SearchString(const char * buffer, int start, const char * subStr, bool matchCase, bool matchWord)
 {
    if(buffer && subStr)
    {
-      char * ptr;
-      char * strBuffer = buffer + start;
+      const char * ptr;
+      const char * strBuffer = buffer + start;
       int subLen = strlen(subStr);
       char beforeChar = start ? *(strBuffer-1) : 0;
-      int (*strcompare)(const char *, const char *, unsigned int) = matchCase ? strncmp : strnicmp;
+      int (*strcompare)(const char *, const char *, uintsize) = matchCase ? strncmp : strnicmp;
 
       for(ptr = strBuffer; *ptr; ptr++)
       {
@@ -761,52 +777,52 @@ public char * SearchString(char * buffer, int start, char * subStr, bool matchCa
          {
             if(matchWord)
             {
-               if(!strcompare(ptr,subStr,subLen) && 
+               if(!strcompare(ptr,subStr,subLen) &&
                   /*
-                  !IS_ALUNDER(ptr[subLen]) && 
+                  !IS_ALUNDER(ptr[subLen]) &&
                   !IS_ALUNDER(beforeChar))
                   */
-                  (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr[subLen])) && 
+                  (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr[subLen])) &&
                   (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(beforeChar)))
-                  return ptr;
+                  return (char *)ptr;
             }
             else
             {
                if(!strcompare(ptr,subStr,subLen))
-                  return ptr;
+                  return (char *)ptr;
             }
          }
-         beforeChar = ptr[0];  
+         beforeChar = ptr[0];
       }
    }
    return null;
 }
 
-public char * RSearchString(char * buffer, char * subStr, int maxLen, bool matchCase, bool matchWord)
+public char * RSearchString(const char * buffer, const char * subStr, int maxLen, bool matchCase, bool matchWord)
 {
    if(buffer && subStr)
    {
       int subLen = strlen(subStr);
-      char * ptr1 = buffer + maxLen - subLen;
-      char * ptr2 = buffer + maxLen - subLen - 1;
-      int (*strcompare)(const char *, const char *, unsigned int) = matchCase ? strncmp : strnicmp;
+      const char * ptr1 = buffer + maxLen - subLen;
+      const char * ptr2 = buffer + maxLen - subLen - 1;
+      int (*strcompare)(const char *, const char *, uintsize) = matchCase ? strncmp : strnicmp;
       for(; ptr1 >=buffer; ptr1--, ptr2--)
       {
          if(tolower(*subStr) == tolower(*ptr1))
          {
             if(matchWord)
             {
-               if(!strcompare(ptr1,subStr,subLen) && 
+               if(!strcompare(ptr1,subStr,subLen) &&
                   //!IS_ALUNDER(ptr1[subLen]) && !IS_ALUNDER(*ptr2))
-                  (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr1[subLen])) && 
+                  (!IS_ALUNDER(subStr[subLen-1]) || !IS_ALUNDER(ptr1[subLen])) &&
                   (!IS_ALUNDER(subStr[0]) || !IS_ALUNDER(*ptr2)))
 
-                 return ptr1;
+                 return (char *)ptr1;
             }
             else
             {
                if(!strcompare(ptr1,subStr,subLen))
-                  return ptr1;
+                  return (char *)ptr1;
             }
          }
       }
@@ -814,65 +830,71 @@ public char * RSearchString(char * buffer, char * subStr, int maxLen, bool match
    return null;
 }
 
-public int Tokenize(char * string, int maxTokens, char* tokens[], bool escapeBackSlashes)
+//public define gnuMakeCharsNeedEscaping = "$%";
+//public define windowsFileNameCharsNotAllowed = "*/:<>?\\\"|";
+//public define linuxFileNameCharsNotAllowed = "/";
+//public define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
+//public define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
+
+// fix #139 to remove " = 2" and warnings for backward compatible calls to Tokenize using 'true' for the 'esc' argument;
+public enum BackSlashEscaping : bool { forArgsPassing = 2 };
+public int Tokenize(char * string, int maxTokens, char* tokens[], BackSlashEscaping esc)
 {
+   const char * escChars, * escCharsQuoted;
    int count = 0;
-   bool quoted = false;
-   byte * start = null;
-   bool escaped = false;
-   char * output = string;
+   bool quoted = false, escaped = false;
+   char * start = null, * output = string;
+   char ch;
+   if(__runtimePlatform == win32)
+   {
+//define windowsFileNameCharsNeedEscaping = " !%&'()+,;=[]^`{}~"; // "#$-.@_" are ok
+      escChars = " !\"%&'()+,;=[]^`{}~"; // windowsFileNameCharsNeedEscaping;
+      escCharsQuoted = "\"";
+   }
+   else
+   {
+//define linuxFileNameCharsNeedEscaping = " !\"$&'()*:;<=>?[\\`{|"; // "#%+,-.@]^_}~" are ok
+      escChars = " !\"$&'()*:;<=>?[\\`{|"; // linuxFileNameCharsNeedEscaping;
+      escCharsQuoted = "\"()$";
+   }
 
-   for(; *string && count < maxTokens; string++, output++)
+   for(; (ch = *string) && count<maxTokens; string++, output++)
    {
+      bool wasEscaped = escaped;
       if(output != string)
-         *output = *string;
+         *output = ch;
       if(start)
       {
          if(escaped)
          {
             escaped = false;
             output--;
-
-            // ADDED THIS HERE...
-            if(output != string)
-               *output = *string;
+            *output = ch;
          }
-         else if(escapeBackSlashes && *string == '\\')
-            escaped = true;
-         else if(*string == '\"')
+         else if(ch == '\"')
          {
-            if(quoted)
-            {
-               *output = '\0';
-               quoted = false;
-            }
-            else
-            {
-               memmove(start + 1, start, string - (char *)start);
-               start++;
-            }
+            quoted ^= true;
+            output--;
          }
-         else if(*string == ' ' && !quoted)
+         else if(ch == ' ' && !quoted)
          {
             tokens[count++] = start;
             *output = '\0';
             start = null;
          }
       }
-      else if(*string != ' ')
+      else if(ch != ' ')
       {
-         if(*string == '\"')
+         if(ch == '\"')
          {
             quoted = true;
-            start = output + 1;
+            start = output+1;
          }
          else
-         {
             start = output;
-            if(*string == '\\' && escapeBackSlashes)
-               escaped = true;
-         }
       }
+      if(!wasEscaped && ch == '\\' && ( esc == true || (esc == forArgsPassing && strchr(quoted ? escCharsQuoted : escChars, *(string+1))) ))
+         escaped = true;
    }
    if(start && count < maxTokens)
    {
@@ -882,11 +904,11 @@ public int Tokenize(char * string, int maxTokens, char* tokens[], bool escapeBac
    return count;
 }
 
-public int TokenizeWith(char * string, int maxTokens, char* tokens[], char * tokenizers, bool escapeBackSlashes)
+public int TokenizeWith(char * string, int maxTokens, char* tokens[], const char * tokenizers, bool escapeBackSlashes)
 {
    int count = 0;
    bool quoted = false;
-   byte * start = null;
+   char * start = null;
    bool escaped = false;
    char * output = string;
    bool quotedFromStart = false;
@@ -952,7 +974,7 @@ public int TokenizeWith(char * string, int maxTokens, char* tokens[], char * tok
    return count;
 }
 
-public char * TrimLSpaces(char * string, char * output)
+public char * TrimLSpaces(const char * string, char * output)
 {
    int c;
    for(c = 0; string[c] && string[c] == ' '; c++);
@@ -960,7 +982,7 @@ public char * TrimLSpaces(char * string, char * output)
    return output;
 }
 
-public char * TrimRSpaces(char * string, char * output)
+public char * TrimRSpaces(const char * string, char * output)
 {
    int c;
    for(c = strlen(string)-1; c >= 0 && string[c] == ' '; c--);
@@ -981,6 +1003,13 @@ public void ChangeCh(char * string, char ch1, char ch2)
       if(string[c] == ch1) string[c] = ch2;
 }
 
+public void ChangeChars(char * string, const char * chars, char alt)
+{
+   int c;
+   for(c=0; string[c]; c++)
+      if(strchr(chars, string[c])) string[c] = alt;
+}
+
 public void RepeatCh(char * string, int count, char ch)
 {
    int c;
@@ -989,7 +1018,7 @@ public void RepeatCh(char * string, int count, char ch)
    string[c] = 0;
 }
 
-public char * CopyString(char * string)
+public char * CopyString(const char * string)
 {
    if(string)
    {
@@ -1040,7 +1069,7 @@ public bool GetString(char ** buffer, char * string, int max)
          {
             c++;
             //result = false;
-            break;            
+            break;
          }
          (*buffer)++;
       }
@@ -1060,13 +1089,13 @@ public uint GetHexValue(char ** buffer)
 {
    char string[20];
    GetString(buffer,string,20);
-   return strtoul(string, null, 16);
+   return (uint)strtoul(string, null, 16);
 }
 
-public char * StripQuotes(char * string, char * output)
+public char * StripQuotes(const char * string, char * output)
 {
    int len;
-   char * src = (string[0] == '\"') ? (string+1) : string;
+   const char * src = (string[0] == '\"') ? (string+1) : string;
    memmove(output, src, strlen(src)+1);
    len = strlen(output);
    if(len && output[len-1] == '\"')
@@ -1074,7 +1103,7 @@ public char * StripQuotes(char * string, char * output)
    return output;
 }
 
-public double FloatFromString(char * string)
+public double FloatFromString(const char * string)
 {
    int c, dig;
    float dec = 0,res = 0;
@@ -1108,7 +1137,7 @@ public double FloatFromString(char * string)
    return neg * res;
 }
 
-public bool IsPathInsideOf(char * path, char * of)
+public bool IsPathInsideOf(const char * path, const char * of)
 {
    if(!path[0] || !of[0])
       return false;  // What to do here? Ever used?