#include "fiko/cekirdek/gecici.hpp" #include #include #include #include #include #include "fiko/core/gunluk.hpp" #ifdef _WIN32 #include #include #else #include #endif namespace fs = std::filesystem; namespace fiko::gecici { namespace { std::mutex g_kilit; std::string g_kokAyar; // kullanıcı değeri (boş = varsayılan) fs::path g_kokCache; // çözülmüş + oluşturulmuş kök std::atomic g_sayac{0}; fs::path exeDizinHesapla() { #ifdef _WIN32 wchar_t yol[MAX_PATH]{}; if (GetModuleFileNameW(nullptr, yol, MAX_PATH)) return fs::path(yol).parent_path(); #else std::error_code ec; const fs::path p = fs::read_symlink("/proc/self/exe", ec); if (!ec) return p.parent_path(); #endif std::error_code ec2; return fs::current_path(ec2); } bool dizinKur(const fs::path& d) { std::error_code ec; if (fs::exists(d, ec)) return fs::is_directory(d, ec); fs::create_directories(d, ec); return !ec; } // Tam yolun kararlı 64-bit özeti (FNV-1a). Büyük/küçük harf ve ayraç normalize — // Windows'ta "C:/a\B.stp" ile "c:\A/b.stp" AYNI önbelleğe düşsün. std::uint64_t yolOzet(std::string_view yol) { std::uint64_t h = 1469598103934665603ull; for (char c : yol) { unsigned char u = (unsigned char)c; if (u == '\\') u = '/'; if (u >= 'A' && u <= 'Z') u = (unsigned char)(u - 'A' + 'a'); h ^= u; h *= 1099511628211ull; } return h; } std::string ozetMetin(std::uint64_t h) { static const char* hx = "0123456789abcdef"; std::string s(16, '0'); for (int i = 15; i >= 0; --i) { s[(size_t)i] = hx[h & 0xF]; h >>= 4; } return s; } // Dosya adını dosya-sistemi güvenli kısa ada indir (ayraç/illegal karakter yok). std::string guvenliAd(std::string_view yol) { std::string ad(yol); if (const size_t s = ad.find_last_of("/\\"); s != std::string::npos) ad = ad.substr(s + 1); for (char& c : ad) if (c == ':' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' || c == '|') c = '_'; if (ad.size() > 64) ad = ad.substr(0, 64); if (ad.empty()) ad = "dosya"; return ad; } struct Girdi { fs::path yol; std::uint64_t boyut = 0; fs::file_time_type zaman{}; }; // Ağaçtaki tüm dosyalar (dizinler hariç). Hata sessizce atlanır. std::vector agacTara(const fs::path& k) { std::vector liste; std::error_code ec; if (!fs::exists(k, ec)) return liste; for (fs::recursive_directory_iterator it(k, fs::directory_options::skip_permission_denied, ec), son; it != son; it.increment(ec)) { if (ec) break; std::error_code e2; if (!it->is_regular_file(e2) || e2) continue; Girdi g; g.yol = it->path(); g.boyut = (std::uint64_t)it->file_size(e2); g.zaman = it->last_write_time(e2); liste.push_back(std::move(g)); } return liste; } std::uint64_t sil(const fs::path& p, std::uint64_t boyut) { std::error_code ec; return fs::remove(p, ec) && !ec ? boyut : 0; } } // namespace const fs::path& uygulamaDizini() { static const fs::path d = exeDizinHesapla(); return d; } fs::path kok() { std::lock_guard kilit(g_kilit); if (!g_kokCache.empty()) return g_kokCache; fs::path aday = g_kokAyar.empty() ? (uygulamaDizini() / "gecici") : fs::path(g_kokAyar); if (!dizinKur(aday)) { // Kurulum dizini salt-okunur olabilir (Program Files) → sistem temp'i son çare. std::error_code ec; const fs::path yedek = fs::temp_directory_path(ec) / "fikocad_gecici"; if (!ec && dizinKur(yedek)) { gunluk::uyari("Geçici dizin oluşturulamadı ({}) → {}", aday.string(), yedek.string()); aday = yedek; } } g_kokCache = aday; return g_kokCache; } void kokAta(const fs::path& yeni) { { std::lock_guard kilit(g_kilit); g_kokAyar = yeni.string(); g_kokCache.clear(); // sonraki kok() yeniden çözer/oluşturur } kok(); } const std::string& kokAyar() { std::lock_guard kilit(g_kilit); return g_kokAyar; } fs::path altDizin(std::string_view ad) { const fs::path k = kok(); if (ad.empty() || ad.find_first_of("/\\:") != std::string_view::npos) return k; const fs::path d = k / fs::path(std::string(ad)); return dizinKur(d) ? d : k; } fs::path onbellekDosyasi(std::string_view kaynakYol, std::string_view uzanti) { std::string ad = guvenliAd(kaynakYol); ad += '-'; ad += ozetMetin(yolOzet(kaynakYol)); ad += '.'; ad += std::string(uzanti); return altDizin("onbellek") / ad; } bool kokIcindeMi(std::string_view yol) { if (yol.empty()) return false; std::error_code ec; const fs::path k = fs::weakly_canonical(kok(), ec); const fs::path p = fs::weakly_canonical(fs::path(std::string(yol)), ec); if (ec) return false; auto ki = k.begin(), pi = p.begin(); for (; ki != k.end() && pi != p.end(); ++ki, ++pi) if (*ki != *pi) return false; return ki == k.end(); // kök, yolun öneki } fs::path geciciDosya(std::string_view onek, std::string_view uzanti) { #ifdef _WIN32 const unsigned pid = (unsigned)_getpid(); #else const unsigned pid = (unsigned)getpid(); #endif std::string ad(onek.empty() ? "gecici" : std::string(onek)); ad += '-'; ad += std::to_string(pid); ad += '-'; ad += std::to_string(g_sayac.fetch_add(1) + 1); if (!uzanti.empty()) { ad += '.'; ad += std::string(uzanti); } return kok() / ad; } std::uint64_t toplamBoyut() { std::uint64_t t = 0; for (const Girdi& g : agacTara(kok())) t += g.boyut; return t; } std::uint64_t buda(const BudamaPolitika& p) { std::vector liste = agacTara(kok()); std::uint64_t silinen = 0, kalan = 0; // 1) Yaş sınırı. if (p.maksGun > 0) { const auto simdi = fs::file_time_type::clock::now(); const auto sinir = std::chrono::hours(24 * (long long)p.maksGun); for (Girdi& g : liste) { if (simdi - g.zaman > sinir) { silinen += sil(g.yol, g.boyut); g.boyut = 0; // ele alındı } } } liste.erase(std::remove_if(liste.begin(), liste.end(), [](const Girdi& g) { return g.boyut == 0; }), liste.end()); for (const Girdi& g : liste) kalan += g.boyut; // 2) Toplam boyut tavanı — en eskiden başlayarak sil. const std::uint64_t tavan = p.maksMb * 1024ull * 1024ull; if (p.maksMb > 0 && kalan > tavan) { std::sort(liste.begin(), liste.end(), [](const Girdi& a, const Girdi& b) { return a.zaman < b.zaman; }); for (const Girdi& g : liste) { if (kalan <= tavan) break; const std::uint64_t s = sil(g.yol, g.boyut); silinen += s; kalan -= s; } } if (silinen) gunluk::bilgi("Geçici budandı: {:.1f} MB silindi ({})", (double)silinen / (1024.0 * 1024.0), kok().string()); return silinen; } std::uint64_t temizle() { std::uint64_t silinen = 0; for (const Girdi& g : agacTara(kok())) silinen += sil(g.yol, g.boyut); // Boşalan alt dizinleri kaldır (kök kalır). std::error_code ec; for (fs::directory_iterator it(kok(), ec), son; it != son; it.increment(ec)) { if (ec) break; std::error_code e2; if (it->is_directory(e2)) fs::remove_all(it->path(), e2); } return silinen; } bool eskiYanDosyaGoc(std::string_view kaynakYol, std::string_view uzanti) { std::error_code ec; const fs::path eski = fs::path(std::string(kaynakYol) + "." + std::string(uzanti)); if (!fs::exists(eski, ec)) return false; const fs::path yeni = onbellekDosyasi(kaynakYol, uzanti); if (fs::exists(yeni, ec)) { // yenisi zaten var → eskisi artık artık fs::remove(eski, ec); return true; } fs::rename(eski, yeni, ec); if (ec) { // farklı birim olabilir → kopyala+sil std::error_code e2; fs::copy_file(eski, yeni, fs::copy_options::overwrite_existing, e2); if (e2) return false; fs::remove(eski, e2); } gunluk::bilgi("Eski yan-önbellek geçici dizine taşındı: {}", eski.string()); return true; } } // namespace fiko::gecici