text: // Copyright © Amer Koleci and Contributors.
  // Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
  
  #include "IO/Path.h"
  #include "Core/Log.h"
  #include "Core/Assert.h"
  #ifdef _WIN32
  #   include "PlatformInclude.h"
  #else
  #   include <unistd.h>
  #   ifdef __linux__
  #       include <linux/limits.h>
  #   endif
  #endif
  
  #include <filesystem>
  namespace fs = std::filesystem;
  
  namespace Alimer::Path
  {
      FilePath Normalize(const FilePath& path)
      {
          FilePath normalized;
  
          auto len = path.length();
          for (auto n = 0; n < len; n++)
          {
              // normalize slashes
              if (path[n] == '\\' || path[n] == '/')
              {
                  if (normalized.length() == 0 || normalized[normalized.length() - 1] != '/')
                      normalized.append("/");
              }
              // move up a directory
              else if (path[n] == '.' && n < len - 1 && path[n + 1] == '.')
              {
                  // search backwards for last /
                  bool could_move_up = false;
                  if (normalized.length() > 0)
                  {
                      for (auto k = normalized.length() - 1; k > 0; k--)
                          if (normalized[k - 1] == '/')
                          {
                              normalized = normalized.substr(0, k - 1);
                              could_move_up = true;
                              break;
                          }
                  }
  
                  if (!could_move_up)
                      normalized.append(".");
                  else
                      n++;
              }
              else
                  normalized += path[n];
          }
  
          return normalized;
      }
  
      FilePath GetCurrentPath()
      {
          auto path = std::filesystem::current_path();
          return Normalize(path.string());
      }
  
      FilePath GetTempDirectoryPath()
      {
          try
          {
              auto path = fs::temp_directory_path();
              return path.string();
          }
          catch (fs::filesystem_error& e)
          {
              LOGW("{}", e.what());
          }
  
          return kEmptyString;
          
      }
  
  #ifndef _WIN32
      FilePath GetExecutablePath()
      {
          pid_t pid = getpid();
          static const char* exts[] = { "exe", "file", "a.out" };
          char link_path[PATH_MAX];
          char target[PATH_MAX];
  
          for (auto* ext : exts)
          {
              snprintf(link_path, sizeof(link_path), "/proc/%u/%s",
                  unsigned(pid), ext);
              ssize_t ret = readlink(link_path, target, sizeof(target) - 1);
              if (ret >= 0)
                  target[ret] = '\0';
  
              return string(target);
          }
  
          return "";
      }
  #endif
  
      FilePath Combine(const FilePath& path1, const FilePath& path2)
      {
          FilePath normalizedPath1 = Normalize(path1);
          FilePath normalizedPath2 = Normalize(path2);
  
          return Normalize(normalizedPath1.append("/").append(normalizedPath2));
      }
  
      FilePath Combine(const FilePath& path1, const FilePath& path2, const FilePath& path3)
      {
          return Path::Combine(path1, Path::Combine(path2, path3));
      }
  }
  