#include "post/fiko_post.hpp" #include #include #include #include #include #include #include #include #include #include namespace fikatia::post { // ================================================================ fikodb ==== namespace fikodb { namespace { [[noreturn]] void hata(const char* mesaj) { throw std::runtime_error(mesaj); } std::uint16_t be16(const std::uint8_t* p) { return static_cast((p[0] << 8) | p[1]); } std::uint32_t be32(const std::uint8_t* p) { return (static_cast(p[0]) << 24) | (static_cast(p[1]) << 16) | (static_cast(p[2]) << 8) | static_cast(p[3]); } void be16Yaz(std::uint8_t* p, std::uint16_t v) { p[0] = static_cast(v >> 8); p[1] = static_cast(v); } void be32Yaz(std::uint8_t* p, std::uint32_t v) { p[0] = static_cast(v >> 24); p[1] = static_cast(v >> 16); p[2] = static_cast(v >> 8); p[3] = static_cast(v); } std::size_t varintCoz(const std::uint8_t* p, const std::uint8_t* son, std::uint64_t& v) { std::uint64_t sonuc = 0; for (int i = 0; i < 8; ++i) { if (p + i >= son) return 0; const std::uint8_t b = p[i]; if (b < 0x80) { v = (sonuc << 7) | b; return static_cast(i) + 1; } sonuc = (sonuc << 7) | (b & 0x7f); } if (p + 8 >= son) return 0; v = (sonuc << 8) | p[8]; return 9; } void varintYaz(std::vector& cikti, std::uint64_t v) { if (v <= 0x7f) { cikti.push_back(static_cast(v)); return; } if (v >> 56) { std::uint8_t b[9]; b[8] = static_cast(v); v >>= 8; for (int i = 7; i >= 0; --i) { b[i] = static_cast((v & 0x7f) | 0x80); v >>= 7; } cikti.insert(cikti.end(), b, b + 9); return; } std::uint8_t tersten[9]; int n = 0; do { tersten[n++] = static_cast(v & 0x7f); v >>= 7; } while (v); for (int i = n - 1; i > 0; --i) cikti.push_back(static_cast(tersten[i] | 0x80)); cikti.push_back(tersten[0]); } std::size_t varintBoyu(std::uint64_t v) { if (v >> 56) return 9; std::size_t n = 0; do { ++n; v >>= 7; } while (v); return n; } std::string buyukHarf(std::string_view s) { std::string r(s); for (char& c : r) c = static_cast(std::toupper(static_cast(c))); return r; } std::string_view kirp(std::string_view s) { const char* bosluk = " \t\r\n"; const std::size_t bas = s.find_first_not_of(bosluk); if (bas == std::string_view::npos) return {}; const std::size_t bit = s.find_last_not_of(bosluk); return s.substr(bas, bit - bas + 1); } // Kayit (record) cozumu — SQLite serial type tablosu. std::vector kayitCoz(const std::uint8_t* p, std::size_t n) { const std::uint8_t* son = p + n; std::uint64_t baslikBoy = 0; const std::size_t k = varintCoz(p, son, baslikBoy); if (!k || baslikBoy < k || baslikBoy > n) hata("fikodb: bozuk kayit basligi"); const std::uint8_t* tipP = p + k; const std::uint8_t* tipSon = p + baslikBoy; const std::uint8_t* govde = tipSon; std::vector degerler; while (tipP < tipSon) { std::uint64_t seri = 0; const std::size_t adim = varintCoz(tipP, tipSon, seri); if (!adim) hata("fikodb: bozuk seri tipi"); tipP += adim; Deger d; if (seri == 0) { d = Deger::bos(); } else if (seri >= 1 && seri <= 6) { static const int boylar[] = {0, 1, 2, 3, 4, 6, 8}; const int boy = boylar[seri]; if (govde + boy > son) hata("fikodb: kayit govdesi tasti"); std::uint64_t u = 0; for (int i = 0; i < boy; ++i) u = (u << 8) | govde[i]; const int kay = 64 - 8 * boy; d = Deger::tamYap(static_cast(u << kay) >> kay); govde += boy; } else if (seri == 7) { if (govde + 8 > son) hata("fikodb: kayit govdesi tasti"); std::uint64_t u = 0; for (int i = 0; i < 8; ++i) u = (u << 8) | govde[i]; double g = 0; std::memcpy(&g, &u, 8); d = Deger::gercekYap(g); govde += 8; } else if (seri == 8 || seri == 9) { d = Deger::tamYap(seri == 8 ? 0 : 1); } else if (seri >= 12) { const std::size_t boy = static_cast((seri - 12) / 2); if (govde + boy > son) hata("fikodb: kayit govdesi tasti"); if (seri % 2 == 0) d = Deger::ikiliYap(std::vector(govde, govde + boy)); else d = Deger::metinYap(std::string(reinterpret_cast(govde), boy)); govde += boy; } else { hata("fikodb: desteklenmeyen seri tipi"); } degerler.push_back(std::move(d)); } return degerler; } struct HamSatir { std::int64_t rowid = 0; std::vector yuk; }; struct Okuyucu { const std::uint8_t* v = nullptr; std::size_t n = 0; std::size_t sayfaBoy = 0; std::size_t kullanilabilir = 0; const std::uint8_t* sayfa(std::uint64_t no) const { if (no == 0 || no * sayfaBoy > n) hata("fikodb: sayfa numarasi dosya disinda"); return v + (no - 1) * sayfaBoy; } std::vector yukTopla(const std::uint8_t* p, const std::uint8_t* sinir, std::uint64_t toplam) const { const std::size_t U = kullanilabilir; const std::size_t X = U - 35; std::size_t yerel; if (toplam <= X) { yerel = static_cast(toplam); } else { const std::size_t M = ((U - 12) * 32) / 255 - 23; const std::size_t K = M + static_cast((toplam - M) % (U - 4)); yerel = (K <= X) ? K : M; } if (p + yerel > sinir) hata("fikodb: hucre sayfa disina tasti"); std::vector yuk(p, p + yerel); if (yerel < toplam) { if (p + yerel + 4 > sinir) hata("fikodb: tasma isaretcisi eksik"); std::uint32_t sonraki = be32(p + yerel); std::uint64_t kalan = toplam - yerel; while (kalan > 0) { if (sonraki == 0) hata("fikodb: tasma zinciri kopuk"); const std::uint8_t* ts = sayfa(sonraki); sonraki = be32(ts); const std::size_t parca = std::min(static_cast(kalan), U - 4); yuk.insert(yuk.end(), ts + 4, ts + 4 + parca); kalan -= parca; } } return yuk; } void tabloTara(std::uint32_t sayfaNo, int derinlik, std::vector& sonuc) const { if (derinlik > 32) hata("fikodb: b-agac cok derin"); const std::uint8_t* s = sayfa(sayfaNo); const std::size_t basOfs = (sayfaNo == 1) ? 100 : 0; const std::uint8_t tip = s[basOfs]; const std::uint16_t hucreSayisi = be16(s + basOfs + 3); if (tip == 5) { const std::uint8_t* ptrDizi = s + basOfs + 12; for (std::uint16_t i = 0; i < hucreSayisi; ++i) { const std::uint16_t ofs = be16(ptrDizi + 2 * i); if (ofs + 4 > sayfaBoy) hata("fikodb: bozuk ic hucre"); tabloTara(be32(s + ofs), derinlik + 1, sonuc); } tabloTara(be32(s + basOfs + 8), derinlik + 1, sonuc); return; } if (tip != 13) hata("fikodb: beklenmeyen sayfa tipi"); const std::uint8_t* ptrDizi = s + basOfs + 8; const std::uint8_t* sinir = s + sayfaBoy; for (std::uint16_t i = 0; i < hucreSayisi; ++i) { const std::uint16_t ofs = be16(ptrDizi + 2 * i); const std::uint8_t* h = s + ofs; std::uint64_t yukBoy = 0, rowid = 0; std::size_t a = varintCoz(h, sinir, yukBoy); if (!a) hata("fikodb: bozuk hucre"); std::size_t b = varintCoz(h + a, sinir, rowid); if (!b) hata("fikodb: bozuk hucre rowid"); HamSatir hs; hs.rowid = static_cast(rowid); hs.yuk = yukTopla(h + a + b, sinir, yukBoy); sonuc.push_back(std::move(hs)); } } }; // ---- yazici ---- std::vector kayitKodla(const std::vector& degerler, int ipkKolon) { std::vector tipler, govde; for (std::size_t i = 0; i < degerler.size(); ++i) { const Deger& d = degerler[i]; if (static_cast(i) == ipkKolon) { // rowid takma adi kayitta NULL yazilir varintYaz(tipler, 0); continue; } switch (d.tip) { case Deger::Tip::Bos: varintYaz(tipler, 0); break; case Deger::Tip::Tam: { const std::int64_t v = d.tam; if (v == 0 || v == 1) { varintYaz(tipler, v == 0 ? 8u : 9u); break; } int boy; if (v >= -128 && v <= 127) boy = 1; else if (v >= -32768 && v <= 32767) boy = 2; else if (v >= -8388608 && v <= 8388607) boy = 3; else if (v >= -2147483648LL && v <= 2147483647LL) boy = 4; else if (v >= -140737488355328LL && v <= 140737488355327LL) boy = 6; else boy = 8; static const std::uint64_t seriler[] = {0, 1, 2, 3, 4, 0, 5, 0, 6}; varintYaz(tipler, seriler[boy]); for (int b = boy - 1; b >= 0; --b) govde.push_back(static_cast(static_cast(v) >> (8 * b))); break; } case Deger::Tip::Gercek: { varintYaz(tipler, 7); std::uint64_t u = 0; std::memcpy(&u, &d.gercek, 8); for (int b = 7; b >= 0; --b) govde.push_back(static_cast(u >> (8 * b))); break; } case Deger::Tip::Metin: varintYaz(tipler, 13 + 2 * static_cast(d.metin.size())); govde.insert(govde.end(), d.metin.begin(), d.metin.end()); break; case Deger::Tip::Ikili: varintYaz(tipler, 12 + 2 * static_cast(d.ikili.size())); govde.insert(govde.end(), d.ikili.begin(), d.ikili.end()); break; } } // baslik uzunlugu kendi varint'ini da icerir — sabitlenene kadar dene std::size_t baslikBoy = 0; for (std::size_t l = 1; l <= 9; ++l) { if (varintBoyu(tipler.size() + l) == l) { baslikBoy = tipler.size() + l; break; } } std::vector kayit; varintYaz(kayit, baslikBoy); kayit.insert(kayit.end(), tipler.begin(), tipler.end()); kayit.insert(kayit.end(), govde.begin(), govde.end()); return kayit; } struct YaziciHucre { std::int64_t rowid = 0; std::vector hucre; // varint(P)+varint(rowid)+yerel[+4B tasma] std::vector> tasmaParca; // her biri <= U-4 std::size_t tasmaPtrOfs = 0; // hucre icindeki 4B isaretci konumu }; YaziciHucre hucreYap(std::int64_t rowid, const std::vector& kayit, std::size_t U) { YaziciHucre h; h.rowid = rowid; const std::size_t P = kayit.size(); const std::size_t X = U - 35; std::size_t yerel; if (P <= X) { yerel = P; } else { const std::size_t M = ((U - 12) * 32) / 255 - 23; const std::size_t K = M + (P - M) % (U - 4); yerel = (K <= X) ? K : M; } varintYaz(h.hucre, P); varintYaz(h.hucre, static_cast(rowid)); h.hucre.insert(h.hucre.end(), kayit.begin(), kayit.begin() + static_cast(yerel)); if (yerel < P) { h.tasmaPtrOfs = h.hucre.size(); h.hucre.insert(h.hucre.end(), 4, 0); std::size_t ofs = yerel; while (ofs < P) { const std::size_t parca = std::min(P - ofs, U - 4); h.tasmaParca.emplace_back(kayit.begin() + static_cast(ofs), kayit.begin() + static_cast(ofs + parca)); ofs += parca; } } return h; } void yaprakSerile(std::vector& s, std::size_t basOfs, const std::vector& hucreler, std::size_t U) { s[basOfs] = 13; be16Yaz(s.data() + basOfs + 1, 0); be16Yaz(s.data() + basOfs + 3, static_cast(hucreler.size())); s[basOfs + 7] = 0; std::size_t icerik = U; for (std::size_t i = 0; i < hucreler.size(); ++i) { icerik -= hucreler[i]->hucre.size(); be16Yaz(s.data() + basOfs + 8 + 2 * i, static_cast(icerik)); std::memcpy(s.data() + icerik, hucreler[i]->hucre.data(), hucreler[i]->hucre.size()); } be16Yaz(s.data() + basOfs + 5, static_cast(icerik)); } struct TabloYerlesim { std::vector hucreler; std::vector> yapraklar; // hucre indeksleri std::vector yaprakSayfalari; std::uint32_t kokSayfa = 0; }; } // namespace Deger Deger::bos() { return {}; } Deger Deger::tamYap(std::int64_t v) { Deger d; d.tip = Tip::Tam; d.tam = v; return d; } Deger Deger::gercekYap(double v) { Deger d; d.tip = Tip::Gercek; d.gercek = v; return d; } Deger Deger::metinYap(std::string v) { Deger d; d.tip = Tip::Metin; d.metin = std::move(v); return d; } Deger Deger::ikiliYap(std::vector v) { Deger d; d.tip = Tip::Ikili; d.ikili = std::move(v); return d; } std::int64_t Deger::tamDeger() const { switch (tip) { case Tip::Tam: return tam; case Tip::Gercek: return static_cast(gercek); case Tip::Metin: return std::strtoll(metin.c_str(), nullptr, 10); default: return 0; } } std::string Deger::metinDeger() const { switch (tip) { case Tip::Metin: return metin; case Tip::Tam: return std::to_string(tam); default: return {}; } } bool Deger::operator==(const Deger& o) const { if (tip != o.tip) return false; switch (tip) { case Tip::Bos: return true; case Tip::Tam: return tam == o.tam; case Tip::Gercek: return gercek == o.gercek; case Tip::Metin: return metin == o.metin; case Tip::Ikili: return ikili == o.ikili; } return false; } int Tablo::kolonIndeksi(std::string_view ad) const { for (std::size_t i = 0; i < kolonlar.size(); ++i) if (kolonlar[i] == ad) return static_cast(i); return -1; } const Deger* Tablo::deger(const Satir& s, std::string_view kolon) const { const int i = kolonIndeksi(kolon); if (i < 0 || static_cast(i) >= s.degerler.size()) return nullptr; return &s.degerler[static_cast(i)]; } Tablo* VeriTabani::tablo(std::string_view isim) { for (auto& t : tablolar) if (t.isim == isim) return &t; return nullptr; } const Tablo* VeriTabani::tablo(std::string_view isim) const { for (auto& t : tablolar) if (t.isim == isim) return &t; return nullptr; } Tablo& VeriTabani::tabloEkle(std::string isim, std::string sql) { Tablo t; t.isim = std::move(isim); t.sql = std::move(sql); t.kolonlar = kolonAyikla(t.sql, &t.ipkKolon); tablolar.push_back(std::move(t)); return tablolar.back(); } std::int64_t VeriTabani::ekle(Tablo& t, std::vector degerler) { // tabloEkle vektoru buyutup referanslari gecersizleyebilir — isimle yeniden bul const std::string isim = t.isim; while (degerler.size() < t.kolonlar.size()) degerler.push_back(Deger::bos()); std::int64_t enBuyuk = 0; for (const auto& s : t.satirlar) enBuyuk = std::max(enBuyuk, s.rowid); const bool otomatik = t.sql.find("AUTOINCREMENT") != std::string::npos; const int ipk = t.ipkKolon; if (otomatik && !tablo("sqlite_sequence")) tabloEkle("sqlite_sequence", "CREATE TABLE sqlite_sequence(name,seq)"); Tablo& hedef = *tablo(isim); Tablo* sira = otomatik ? tablo("sqlite_sequence") : nullptr; Satir* siraSatir = nullptr; if (sira) { for (auto& s : sira->satirlar) { const Deger* ad = sira->deger(s, "name"); if (ad && ad->metinDeger() == isim) { siraSatir = &s; enBuyuk = std::max(enBuyuk, sira->deger(s, "seq")->tamDeger()); break; } } } std::int64_t rowid = enBuyuk + 1; if (ipk >= 0 && degerler[static_cast(ipk)].tip == Deger::Tip::Tam) rowid = degerler[static_cast(ipk)].tam; if (ipk >= 0) degerler[static_cast(ipk)] = Deger::tamYap(rowid); if (sira) { if (siraSatir) { siraSatir->degerler[1] = Deger::tamYap(std::max(siraSatir->degerler[1].tamDeger(), rowid)); } else { Satir s; s.rowid = static_cast(sira->satirlar.size()) + 1; s.degerler = {Deger::metinYap(isim), Deger::tamYap(rowid)}; sira->satirlar.push_back(std::move(s)); } } Satir s; s.rowid = rowid; s.degerler = std::move(degerler); hedef.satirlar.push_back(std::move(s)); return rowid; } std::vector kolonAyikla(std::string_view createSql, int* ipkKolon) { if (ipkKolon) *ipkKolon = -1; std::vector kolonlar; const std::size_t ac = createSql.find('('); const std::size_t kap = createSql.rfind(')'); if (ac == std::string_view::npos || kap == std::string_view::npos || kap <= ac) return kolonlar; const std::string_view ic = createSql.substr(ac + 1, kap - ac - 1); std::vector parcalar; int derinlik = 0; char tirnak = 0; std::size_t bas = 0; for (std::size_t i = 0; i < ic.size(); ++i) { const char c = ic[i]; if (tirnak) { if ((tirnak == '[' && c == ']') || (tirnak != '[' && c == tirnak)) tirnak = 0; continue; } if (c == '\'' || c == '"' || c == '`' || c == '[') tirnak = c; else if (c == '(') ++derinlik; else if (c == ')') --derinlik; else if (c == ',' && derinlik == 0) { parcalar.push_back(ic.substr(bas, i - bas)); bas = i + 1; } } parcalar.push_back(ic.substr(bas)); for (const std::string_view hamParca : parcalar) { const std::string_view parca = kirp(hamParca); if (parca.empty()) continue; std::string isim; if (parca[0] == '"' || parca[0] == '\'' || parca[0] == '`' || parca[0] == '[') { const char kapanis = parca[0] == '[' ? ']' : parca[0]; const std::size_t son = parca.find(kapanis, 1); if (son == std::string_view::npos) continue; isim = std::string(parca.substr(1, son - 1)); } else { std::size_t son = 0; while (son < parca.size() && !std::isspace(static_cast(parca[son])) && parca[son] != '(') ++son; isim = std::string(parca.substr(0, son)); } const std::string buyuk = buyukHarf(isim); if (buyuk == "PRIMARY" || buyuk == "UNIQUE" || buyuk == "CHECK" || buyuk == "FOREIGN" || buyuk == "CONSTRAINT") continue; // tablo kisiti, kolon degil const std::string parcaBuyuk = buyukHarf(parca); if (ipkKolon && *ipkKolon < 0 && parcaBuyuk.find("INTEGER") != std::string::npos && parcaBuyuk.find("PRIMARY KEY") != std::string::npos) *ipkKolon = static_cast(kolonlar.size()); kolonlar.push_back(std::move(isim)); } return kolonlar; } VeriTabani coz(const std::uint8_t* veri, std::size_t boyut) { if (boyut < 100 || std::memcmp(veri, "SQLite format 3", 16) != 0) hata("fikodb: SQLite imzasi yok"); Okuyucu o; o.v = veri; o.n = boyut; o.sayfaBoy = be16(veri + 16); if (o.sayfaBoy == 1) o.sayfaBoy = 65536; if (o.sayfaBoy < 512) hata("fikodb: gecersiz sayfa boyu"); o.kullanilabilir = o.sayfaBoy - veri[20]; if (be32(veri + 56) != 1) hata("fikodb: yalniz UTF-8 destekleniyor"); std::vector ana; o.tabloTara(1, 0, ana); VeriTabani vt; for (const HamSatir& hs : ana) { std::vector d = kayitCoz(hs.yuk.data(), hs.yuk.size()); if (d.size() < 5 || d[0].metinDeger() != "table") continue; Tablo& t = vt.tabloEkle(d[1].metinDeger(), d[4].metinDeger()); const std::uint32_t kok = static_cast(d[3].tamDeger()); if (kok == 0) continue; std::vector ham; o.tabloTara(kok, 0, ham); for (HamSatir& h : ham) { Satir s; s.rowid = h.rowid; s.degerler = kayitCoz(h.yuk.data(), h.yuk.size()); // eski satirlar sonradan eklenen kolonlari icermeyebilir (geriye uyum) while (s.degerler.size() < t.kolonlar.size()) s.degerler.push_back(Deger::bos()); if (t.ipkKolon >= 0 && s.degerler[static_cast(t.ipkKolon)].bosMu()) s.degerler[static_cast(t.ipkKolon)] = Deger::tamYap(s.rowid); t.satirlar.push_back(std::move(s)); } std::sort(t.satirlar.begin(), t.satirlar.end(), [](const Satir& a, const Satir& b) { return a.rowid < b.rowid; }); } return vt; } VeriTabani dosyadanCoz(const std::string& yol) { std::ifstream f(std::filesystem::path(yol), std::ios::binary); if (!f) hata("fikodb: dosya acilamadi"); std::vector veri((std::istreambuf_iterator(f)), std::istreambuf_iterator()); return coz(veri.data(), veri.size()); } std::vector yaz(const VeriTabani& vt) { constexpr std::size_t U = 4096; std::vector yerlesimler(vt.tablolar.size()); for (std::size_t ti = 0; ti < vt.tablolar.size(); ++ti) { const Tablo& t = vt.tablolar[ti]; TabloYerlesim& y = yerlesimler[ti]; std::vector sirali = t.satirlar; std::sort(sirali.begin(), sirali.end(), [](const Satir& a, const Satir& b) { return a.rowid < b.rowid; }); for (const Satir& s : sirali) y.hucreler.push_back(hucreYap(s.rowid, kayitKodla(s.degerler, t.ipkKolon), U)); // yapraklara paketle std::vector yaprak; std::size_t dolu = 0; const std::size_t kapasite = U - 8; for (std::size_t i = 0; i < y.hucreler.size(); ++i) { const std::size_t maliyet = y.hucreler[i].hucre.size() + 2; if (!yaprak.empty() && dolu + maliyet > kapasite) { y.yapraklar.push_back(std::move(yaprak)); yaprak.clear(); dolu = 0; } yaprak.push_back(i); dolu += maliyet; } y.yapraklar.push_back(std::move(yaprak)); // bos tablo da tek bos yaprak alir } // sayfa numaralari: 1=sqlite_master, sonra tablo b-agaclari, en sonda tasmalar std::uint32_t sonraki = 1; for (auto& y : yerlesimler) { for (std::size_t i = 0; i < y.yapraklar.size(); ++i) y.yaprakSayfalari.push_back(++sonraki); y.kokSayfa = (y.yapraklar.size() == 1) ? y.yaprakSayfalari[0] : ++sonraki; } for (auto& y : yerlesimler) { for (auto& h : y.hucreler) { if (h.tasmaParca.empty()) continue; be32Yaz(h.hucre.data() + h.tasmaPtrOfs, sonraki + 1); sonraki += static_cast(h.tasmaParca.size()); } } const std::uint32_t toplamSayfa = sonraki; std::vector cikti(static_cast(toplamSayfa) * U, 0); // tablo yapraklari + ic (kok) sayfalar for (const auto& y : yerlesimler) { for (std::size_t li = 0; li < y.yapraklar.size(); ++li) { std::vector hp; for (std::size_t hi : y.yapraklar[li]) hp.push_back(&y.hucreler[hi]); std::vector sayfa(U, 0); yaprakSerile(sayfa, 0, hp, U); std::memcpy(cikti.data() + static_cast(y.yaprakSayfalari[li] - 1) * U, sayfa.data(), U); } if (y.yapraklar.size() > 1) { std::uint8_t* s = cikti.data() + static_cast(y.kokSayfa - 1) * U; s[0] = 5; be16Yaz(s + 1, 0); const std::size_t icSayisi = y.yapraklar.size() - 1; be16Yaz(s + 3, static_cast(icSayisi)); s[7] = 0; be32Yaz(s + 8, y.yaprakSayfalari.back()); std::size_t icerik = U; for (std::size_t i = 0; i < icSayisi; ++i) { std::vector hucre; const auto& yaprak = y.yapraklar[i]; const std::int64_t sonRowid = y.hucreler[yaprak.back()].rowid; hucre.resize(4); be32Yaz(hucre.data(), y.yaprakSayfalari[i]); varintYaz(hucre, static_cast(sonRowid)); icerik -= hucre.size(); if (icerik < 12 + 2 * icSayisi) hata("fikodb: tablo tek ic sayfaya sigmadi"); be16Yaz(s + 12 + 2 * i, static_cast(icerik)); std::memcpy(s + icerik, hucre.data(), hucre.size()); } be16Yaz(s + 5, static_cast(icerik)); } } // tasma sayfalari for (const auto& y : yerlesimler) { for (const auto& h : y.hucreler) { if (h.tasmaParca.empty()) continue; std::uint32_t no = be32(h.hucre.data() + h.tasmaPtrOfs); for (std::size_t pi = 0; pi < h.tasmaParca.size(); ++pi) { std::uint8_t* s = cikti.data() + static_cast(no - 1) * U; const bool sonParca = (pi + 1 == h.tasmaParca.size()); be32Yaz(s, sonParca ? 0 : no + 1); std::memcpy(s + 4, h.tasmaParca[pi].data(), h.tasmaParca[pi].size()); ++no; } } } // sqlite_master (sayfa 1) { std::vector anaHucreler; for (std::size_t ti = 0; ti < vt.tablolar.size(); ++ti) { const Tablo& t = vt.tablolar[ti]; std::vector d = {Deger::metinYap("table"), Deger::metinYap(t.isim), Deger::metinYap(t.isim), Deger::tamYap(static_cast(yerlesimler[ti].kokSayfa)), Deger::metinYap(t.sql)}; anaHucreler.push_back(hucreYap(static_cast(ti) + 1, kayitKodla(d, -1), U)); if (!anaHucreler.back().tasmaParca.empty()) hata("fikodb: sema metni cok uzun"); } std::size_t toplam = 0; for (const auto& h : anaHucreler) toplam += h.hucre.size() + 2; if (toplam > U - 8 - 100) hata("fikodb: sema tek sayfaya sigmadi"); std::vector sayfa(U, 0); std::vector hp; for (const auto& h : anaHucreler) hp.push_back(&h); yaprakSerile(sayfa, 100, hp, U); std::memcpy(cikti.data(), sayfa.data(), U); } // dosya basligi std::uint8_t* b = cikti.data(); std::memcpy(b, "SQLite format 3", 16); be16Yaz(b + 16, static_cast(U)); b[18] = 1; // legacy yaz/oku surumleri b[19] = 1; b[20] = 0; b[21] = 64; b[22] = 32; b[23] = 32; be32Yaz(b + 24, 1); // degisim sayaci be32Yaz(b + 28, toplamSayfa); be32Yaz(b + 40, 1); // sema cerezi be32Yaz(b + 44, 4); // sema formati (seri tip 8/9 icin >= 4 sart) be32Yaz(b + 56, 1); // UTF-8 be32Yaz(b + 92, 1); // version-valid-for = degisim sayaci be32Yaz(b + 96, 3045000); return cikti; } void dosyayaYaz(const VeriTabani& vt, const std::string& yol) { const std::vector veri = yaz(vt); std::ofstream f(std::filesystem::path(yol), std::ios::binary | std::ios::trunc); if (!f) hata("fikodb: dosya yazilamadi"); f.write(reinterpret_cast(veri.data()), static_cast(veri.size())); } } // namespace fikodb // =========================================================== yardimcilar ==== namespace { bool suruculu(std::string_view s) { return s.size() >= 2 && s[1] == ':' && std::isalpha(static_cast(s[0])); } bool ayracMi(char c) { return c == '\\' || c == '/'; } std::string_view kirpSV(std::string_view s) { const char* bosluk = " \t\r\n"; const std::size_t bas = s.find_first_not_of(bosluk); if (bas == std::string_view::npos) return {}; const std::size_t bit = s.find_last_not_of(bosluk); return s.substr(bas, bit - bas + 1); } std::string buyut(std::string_view s) { std::string r(s); for (char& c : r) c = static_cast(std::toupper(static_cast(c))); return r; } } // namespace std::string yolBirlestir(std::string_view a, std::string_view b) { // ntpath.join davranisi: b'nin suruculu/mutlak olmasi a'yi ezer. if (suruculu(b)) return std::string(b); if (!b.empty() && ayracMi(b[0])) { if (suruculu(a)) return std::string(a.substr(0, 2)) + std::string(b); return std::string(b); } std::string sonuc(a); if (!sonuc.empty() && !ayracMi(sonuc.back()) && sonuc.back() != ':') sonuc += '\\'; sonuc += b; return sonuc; } std::string tumunuDegistir(std::string metin, std::string_view eski, std::string_view yeni) { if (eski.empty()) return metin; std::size_t pos = 0; while ((pos = metin.find(eski, pos)) != std::string::npos) { metin.replace(pos, eski.size(), yeni); pos += yeni.size(); } return metin; } std::string zamanMetni(const std::tm& t, long mikrosaniye) { char b[64]; std::snprintf(b, sizeof(b), "%04d-%02d-%02d %02d:%02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); std::string sonuc(b); if (mikrosaniye > 0) { // Python str(datetime): mikrosaniye 0 ise yazilmaz std::snprintf(b, sizeof(b), ".%06ld", mikrosaniye); sonuc += b; } return sonuc; } std::string simdiMetni() { const auto simdi = std::chrono::system_clock::now(); const std::time_t saniye = std::chrono::system_clock::to_time_t(simdi); std::tm t{}; localtime_s(&t, &saniye); const auto mikro = std::chrono::duration_cast( simdi.time_since_epoch()) .count() % 1000000; return zamanMetni(t, static_cast(mikro < 0 ? 0 : mikro)); } // ================================================================ Tezgah ==== bool Tezgah::operator==(const Tezgah& o) const { return id == o.id && ismi == o.ismi && kuralDosyasi == o.kuralDosyasi && kureMerkezi == o.kureMerkezi && postUzantisi == o.postUzantisi && postDuzenlemeProgrami == o.postDuzenlemeProgrami && tezgahKlasoruAktif == o.tezgahKlasoruAktif && tezgahKlasoru == o.tezgahKlasoru && tezgahProgramiAktif == o.tezgahProgramiAktif && tezgahProgrami == o.tezgahProgrami && ncGondermeProgrami == o.ncGondermeProgrami && tezgahTemizleProgrami == o.tezgahTemizleProgrami && tezgahFtpAktif == o.tezgahFtpAktif && ftpSunucuAdresi == o.ftpSunucuAdresi && ftpPortNo == o.ftpPortNo && ftpKullaniciAdi == o.ftpKullaniciAdi && ftpSifresi == o.ftpSifresi && siraNo == o.siraNo; } std::vector Tezgah::gonderimPlani() const { // ITezgah.dosyaGonder sirasi: klasor kopyalama -> gonderme programi -> FTP std::vector plan; if (tezgahKlasoruAktif) plan.push_back(GonderimYolu::KlasoreKopyala); if (tezgahProgramiAktif) plan.push_back(GonderimYolu::ProgramlaGonder); if (tezgahFtpAktif) plan.push_back(GonderimYolu::FtpYukle); return plan; } TezgahKlasorDurumu Tezgah::acilisKontrol(bool klasorVarMi, bool programVarMi) const { if (!tezgahKlasoruAktif && !tezgahProgramiAktif && !tezgahFtpAktif) return TezgahKlasorDurumu::aktifYontemYok; if (tezgahKlasoruAktif && !klasorVarMi) return TezgahKlasorDurumu::klasorYok; if (tezgahProgramiAktif && !programVarMi) return TezgahKlasorDurumu::tezgahProgramiYok; return TezgahKlasorDurumu::tamam; } // ============================================================= Tezgahlar ==== namespace { std::optional metinAl(const fikodb::Tablo& t, const fikodb::Satir& s, std::string_view kolon) { const fikodb::Deger* d = t.deger(s, kolon); if (!d || d->bosMu()) return std::nullopt; return d->metinDeger(); } std::int64_t tamAl(const fikodb::Tablo& t, const fikodb::Satir& s, std::string_view kolon, std::int64_t varsayilan) { const fikodb::Deger* d = t.deger(s, kolon); if (!d || d->bosMu()) return varsayilan; return d->tamDeger(); } Tezgah tezgahYap(const fikodb::Tablo& t, const fikodb::Satir& s) { Tezgah z; z.id = tamAl(t, s, "id", s.rowid); if (auto v = metinAl(t, s, "ismi")) z.ismi = *v; if (auto v = metinAl(t, s, "kuralDosyasi")) z.kuralDosyasi = *v; z.kureMerkezi = tamAl(t, s, "kureMerkezi", 0); if (auto v = metinAl(t, s, "postUzantisi")) z.postUzantisi = *v; z.postDuzenlemeProgrami = metinAl(t, s, "postDuzenlemeProgrami"); z.tezgahKlasoruAktif = tamAl(t, s, "tezgahKlasoruAktif", 0); z.tezgahKlasoru = metinAl(t, s, "tezgahKlasoru"); z.tezgahProgramiAktif = tamAl(t, s, "tezgahProgramiAktif", 0); z.tezgahProgrami = metinAl(t, s, "tezgahProgrami"); z.ncGondermeProgrami = metinAl(t, s, "ncGondermeProgrami"); z.tezgahTemizleProgrami = metinAl(t, s, "tezgahTemizleProgrami"); z.tezgahFtpAktif = tamAl(t, s, "tezgahFtpAktif", 0); z.ftpSunucuAdresi = metinAl(t, s, "ftpSunucuAdresi"); z.ftpPortNo = metinAl(t, s, "ftpPortNo"); z.ftpKullaniciAdi = metinAl(t, s, "ftpKullaniciAdi"); z.ftpSifresi = metinAl(t, s, "ftpSifresi"); const fikodb::Deger* sn = t.deger(s, "siraNo"); if (sn && !sn->bosMu()) z.siraNo = sn->tamDeger(); return z; } } // namespace Tezgahlar Tezgahlar::veriden(const std::uint8_t* veri, std::size_t boyut) { Tezgahlar sonuc; const fikodb::VeriTabani vt = fikodb::coz(veri, boyut); const fikodb::Tablo* t = vt.tablo("tezgahlar"); if (!t) throw std::runtime_error("tezgahlar tablosu bulunamadi"); for (const auto& s : t->satirlar) sonuc.tumu_.push_back(tezgahYap(*t, s)); // ORDER BY siraNo: NULL en once, esitlikte rowid sirasi korunur std::stable_sort(sonuc.tumu_.begin(), sonuc.tumu_.end(), [](const Tezgah& a, const Tezgah& b) { if (a.siraNo.has_value() != b.siraNo.has_value()) return !a.siraNo.has_value(); if (!a.siraNo) return false; return *a.siraNo < *b.siraNo; }); return sonuc; } Tezgahlar Tezgahlar::dosyadan(const std::string& yol) { std::ifstream f(std::filesystem::path(yol), std::ios::binary); if (!f) throw std::runtime_error("tezgah profili acilamadi: " + yol); std::vector veri((std::istreambuf_iterator(f)), std::istreambuf_iterator()); return veriden(veri.data(), veri.size()); } std::optional Tezgahlar::item(std::string_view ismi) const { for (const auto& t : tumu_) if (t.ismi == ismi) return t; return std::nullopt; } std::string Tezgahlar::metin() const { std::string s = "["; for (std::size_t i = 0; i < tumu_.size(); ++i) { if (i) s += ", "; s += "'" + tumu_[i].ismi + "'"; } return s + "]"; } // ============================================================== FikoPost ==== namespace { // IFikoPost.tezgah'in yarattigi tabloyla ayni sema (Python birebir) const char* const HAREKETLER_SQL = "CREATE TABLE hareketler\n" " (\n" " _id INTEGER PRIMARY KEY AUTOINCREMENT,\n" " modul TEXT,\n" " olay TEXT,\n" " veri TEXT,\n" " saat DATETIME\n" " )"; void hareketEkle(fikodb::VeriTabani& vt, fikodb::Tablo& t, const std::string& tezgahIsmi) { vt.ekle(t, {fikodb::Deger::bos(), fikodb::Deger::metinYap("IFikoPost"), fikodb::Deger::metinYap("tezgahSecildi"), fikodb::Deger::metinYap(tezgahIsmi), fikodb::Deger::metinYap(simdiMetni())}); } } // namespace FikoPost::FikoPost(std::string processKlasoru, std::string ismi, Tezgahlar tezgahlar) : processKlasoru_(std::move(processKlasoru)), ismi_(std::move(ismi)), tezgahlar_(std::move(tezgahlar)) {} std::string FikoPost::hareketlerYolu() const { namespace fs = std::filesystem; const fs::path klasor = fs::path(processKlasoru_) / "Fikomantasyon"; fs::create_directories(klasor); return (klasor / "Fikomantasyon.fikodb").string(); } std::optional FikoPost::tezgah() { const std::string yol = hareketlerYolu(); if (!std::filesystem::exists(yol)) { if (tezgahlar_.sayisi() == 0) return std::nullopt; fikodb::VeriTabani vt; vt.tabloEkle("hareketler", HAREKETLER_SQL); hareketEkle(vt, *vt.tablo("hareketler"), tezgahlar_.tezgahlarTumu()[0].ismi); fikodb::dosyayaYaz(vt, yol); } fikodb::VeriTabani vt = fikodb::dosyadanCoz(yol); fikodb::Tablo* t = vt.tablo("hareketler"); if (!t) return std::nullopt; const fikodb::Satir* son = nullptr; for (const auto& s : t->satirlar) { const fikodb::Deger* modul = t->deger(s, "modul"); const fikodb::Deger* olay = t->deger(s, "olay"); if (modul && olay && modul->metinDeger() == "IFikoPost" && olay->metinDeger() == "tezgahSecildi" && (!son || s.rowid > son->rowid)) son = &s; } if (!son) { if (tezgahlar_.sayisi() == 0) return std::nullopt; hareketEkle(vt, *t, tezgahlar_.tezgahlarTumu()[0].ismi); fikodb::dosyayaYaz(vt, yol); return tezgahlar_.tezgahlarTumu()[0]; } const fikodb::Deger* veri = t->deger(*son, "veri"); return tezgahlar_.item(veri ? veri->metinDeger() : std::string()); } void FikoPost::tezgahSec(const std::string& ismi) { const std::string yol = hareketlerYolu(); fikodb::VeriTabani vt; if (std::filesystem::exists(yol)) { vt = fikodb::dosyadanCoz(yol); } if (!vt.tablo("hareketler")) vt.tabloEkle("hareketler", HAREKETLER_SQL); hareketEkle(vt, *vt.tablo("hareketler"), ismi); fikodb::dosyayaYaz(vt, yol); } std::vector FikoPost::ncDonusumKomutlari(const PostAyarlari& a, const Tezgah& t) const { // uretilenApteleriNcKodaCevir'in komut kurma dongusu — dizgi birlesimi Python // birebir (cevirici program yolunda tirnak olmamasi dahil). std::vector sonuc; for (const std::string& apt : aptler) { const std::string kural = "\"" + yolBirlestir(yolBirlestir(a.scriptYolu, a.ncKuralDosyasiKlasoru), t.kuralDosyasi) + "\""; const std::string aptDosyasi = "\"" + apt + "\""; std::string ncDosyasi; if (a.ncCiktiKlasoruVarsayilan) { const std::size_t ayrim = apt.find_last_of("\\/"); const std::string klasor = (ayrim == std::string::npos) ? "" : apt.substr(0, ayrim); const std::string taban = (ayrim == std::string::npos) ? apt : apt.substr(ayrim + 1); ncDosyasi = "\"" + yolBirlestir(klasor, tumunuDegistir(taban, "." + a.aptUzantisi, "")) + "." + t.postUzantisi + "\""; } else { ncDosyasi = "\"" + yolBirlestir(a.ncCiktiKlasoru, ismi_ + "." + t.postUzantisi) + "\""; } const std::string parametreler = kural + " " + aptDosyasi + " " + ncDosyasi + " " + a.ncCeviriciParametreler; NcDonusumKomut k; k.komut = yolBirlestir(a.scriptYolu, a.ncCeviriciProgram) + " " + parametreler; k.ncDosyasi = ncDosyasi; sonuc.push_back(std::move(k)); } return sonuc; } std::optional FikoPost::postDuzenlemeKomutu(const PostAyarlari& a, const Tezgah& t, const std::string& sonNcDosyasi) { if (!t.postDuzenlemeProgrami) return std::nullopt; return "python \"" + yolBirlestir(a.scriptYolu, *t.postDuzenlemeProgrami) + "\" " + sonNcDosyasi; } std::vector FikoPost::donusumSonrasiTemizlik() { return {"log", "FikoApt_LOG", "LOG"}; } // ============================================================== FikoDBNC ==== namespace { // IFikoDBNC.veriTabaniYarat'in yarattigi tabloyla ayni sema (Python birebir) const char* const POSTLAR_SQL = "CREATE TABLE POSTLAR\n" " (\n" " _id INTEGER PRIMARY KEY AUTOINCREMENT,\n" " ISMI TEXT,\n" " ACIKLAMA TEXT,\n" " KOD BLOB,\n" " EK_BILGILER BLOB,\n" " RESIM BLOB,\n" " saat DATETIME\n" " )"; fikodb::Deger secimliMetin(const std::optional& v) { return v ? fikodb::Deger::metinYap(*v) : fikodb::Deger::bos(); } } // namespace FikoDBNC::FikoDBNC(std::string dbKlasoru, std::string processIsmi) : dbKlasoru_(std::move(dbKlasoru)), processIsmi_(std::move(processIsmi)) {} std::string FikoDBNC::dosyaIsmi(const std::string& processIsmi, const std::tm& t) { const std::string taban = processIsmi.substr(0, processIsmi.find('.')); char b[64]; std::snprintf(b, sizeof(b), "[%02d.%02d.%04d-%02d.%02d.%02d]", t.tm_mday, t.tm_mon + 1, t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec); return taban + b + ".fikoPostVT"; } void FikoDBNC::veriTabaniYarat() { const std::time_t saniye = std::time(nullptr); std::tm t{}; localtime_s(&t, &saniye); dosyaYolu_ = yolBirlestir(dbKlasoru_, dosyaIsmi(processIsmi_, t)); vt_ = {}; vt_.tabloEkle("POSTLAR", POSTLAR_SQL); // AUTOINCREMENT'li tabloyla birlikte sqlite da bu tabloyu yaratir (geriye uyum) vt_.tabloEkle("sqlite_sequence", "CREATE TABLE sqlite_sequence(name,seq)"); fikodb::dosyayaYaz(vt_, dosyaYolu_); acik_ = true; } void FikoDBNC::veriEkle(const std::optional& ismi, const std::optional& aciklama, const std::vector& kod, const std::optional& ekBilgiler) { if (!acik_) throw std::runtime_error("FikoDBNC: once veriTabaniYarat cagrilmali"); fikodb::Tablo* t = vt_.tablo("POSTLAR"); vt_.ekle(*t, {fikodb::Deger::bos(), secimliMetin(ismi), secimliMetin(aciklama), fikodb::Deger::ikiliYap(kod), secimliMetin(ekBilgiler), fikodb::Deger::bos(), fikodb::Deger::metinYap(simdiMetni())}); fikodb::dosyayaYaz(vt_, dosyaYolu_); // Python her veriEkle'de commit ediyordu } bool FikoDBNC::programEkle(const NcProgram& prg, const Tezgah& tezgah) { const std::optional ekBilgi = (tezgah.kureMerkezi == 1) ? std::optional("{kureMerkezi:True}") : std::nullopt; std::ifstream f(std::filesystem::path(prg.apt), std::ios::binary); if (!f) return false; std::vector kodlar((std::istreambuf_iterator(f)), std::istreambuf_iterator()); veriEkle(prg.ismi, prg.aciklama, kodlar, ekBilgi); return true; } std::vector FikoDBNC::programSonrasiTemizlik() { return {"FikoApt", "log", "FikoApt_LOG", "LOG"}; } // ============================================================== APT / NC ==== AptKomut aptSatirAyristir(std::string_view satir) { AptKomut k; const std::string_view s = kirpSV(satir); if (s.empty()) return k; if (s.size() >= 2 && s[0] == '$' && s[1] == '$') { k.tip = AptTip::Yorum; k.metin = std::string(kirpSV(s.substr(2))); return k; } std::size_t kelimeSon = 0; while (kelimeSon < s.size() && !std::isspace(static_cast(s[kelimeSon])) && s[kelimeSon] != '/') ++kelimeSon; const std::string bas = buyut(kirpSV(s.substr(0, kelimeSon))); if (bas == "PARTNO" || bas == "PPRINT" || bas == "INSERT") { k.kelime = bas; k.tip = (bas == "PARTNO") ? AptTip::Partno : (bas == "PPRINT" ? AptTip::Pprint : AptTip::Insert); std::size_t metinBas = kelimeSon; while (metinBas < s.size() && s[metinBas] == ' ') ++metinBas; k.metin = std::string(s.substr(metinBas)); return k; } const std::size_t egik = s.find('/'); k.kelime = buyut(kirpSV(s.substr(0, egik))); if (egik != std::string_view::npos) { std::string_view kalan = s.substr(egik + 1); std::size_t basPos = 0; for (std::size_t i = 0; i <= kalan.size(); ++i) { if (i == kalan.size() || kalan[i] == ',') { const std::string_view parca = kirpSV(kalan.substr(basPos, i - basPos)); basPos = i + 1; if (parca.empty()) continue; const std::string metin(parca); char* son = nullptr; const double deger = std::strtod(metin.c_str(), &son); if (son && *son == '\0') k.sayilar.push_back(deger); else k.secenekler.push_back(buyut(metin)); } } } if (k.kelime == "RAPID") k.tip = AptTip::Rapid; else if (k.kelime == "GOTO") k.tip = AptTip::Goto; else if (k.kelime == "FEDRAT") k.tip = AptTip::Fedrat; else if (k.kelime == "SPINDL") k.tip = AptTip::Spindl; else if (k.kelime == "LOADTL") k.tip = AptTip::Loadtl; else if (k.kelime == "COOLNT") k.tip = AptTip::Coolnt; else if (k.kelime == "END") k.tip = AptTip::End; else if (k.kelime == "FINI") k.tip = AptTip::Fini; else k.tip = AptTip::Diger; return k; } std::vector aptAyristir(std::string_view icerik) { std::vector komutlar; std::string birikmis; std::size_t bas = 0; while (bas <= icerik.size()) { std::size_t son = icerik.find('\n', bas); if (son == std::string_view::npos) son = icerik.size(); std::string_view satir = kirpSV(icerik.substr(bas, son - bas)); bas = son + 1; if (satir.empty() && birikmis.empty()) { if (son >= icerik.size()) break; continue; } const bool yorum = satir.size() >= 2 && satir[0] == '$' && satir[1] == '$'; if (!yorum && !satir.empty() && satir.back() == '$') { // satir devami birikmis += std::string(satir.substr(0, satir.size() - 1)); if (son >= icerik.size()) break; continue; } birikmis += std::string(satir); if (!birikmis.empty()) komutlar.push_back(aptSatirAyristir(birikmis)); birikmis.clear(); if (son >= icerik.size()) break; } return komutlar; } std::string NcUretici::uret(const std::vector& komutlar) { std::string cikti = "%" + bicim_.satirSonu; int satirNo = 0; const auto satir = [&](const std::string& icerik) { ++satirNo; cikti += "N" + std::to_string(satirNo) + " " + icerik + bicim_.satirSonu; }; const auto koordinat = [&](double v) { char b[64]; std::snprintf(b, sizeof(b), "%.*f", bicim_.koordinatHane, v); return std::string(b); }; const auto sayiFS = [](double v) { // F/S: tamsa ondaliksiz char b[64]; if (std::abs(v - std::llround(v)) < 1e-9) std::snprintf(b, sizeof(b), "%lld", static_cast(std::llround(v))); else std::snprintf(b, sizeof(b), "%.3f", v); return std::string(b); }; const auto secenekVar = [](const AptKomut& k, std::string_view ad) { return std::find(k.secenekler.begin(), k.secenekler.end(), ad) != k.secenekler.end(); }; satir(bicim_.baslik); satir("(" + bicim_.islemciNotu + ")"); bool rapid = false, fBekliyor = false, fYazildi = false; int sonG = -1; std::optional f; std::optional son[3]; for (const AptKomut& k : komutlar) { switch (k.tip) { case AptTip::Partno: case AptTip::Pprint: satir("(" + k.metin + ")"); break; case AptTip::Insert: satir(k.metin); break; case AptTip::Loadtl: if (!k.sayilar.empty()) satir("T" + std::to_string(static_cast(k.sayilar[0])) + " M06"); break; case AptTip::Spindl: if (!k.sayilar.empty()) satir("S" + sayiFS(k.sayilar[0]) + (secenekVar(k, "CCLW") ? " M04" : " M03")); break; case AptTip::Fedrat: if (!k.sayilar.empty()) { f = k.sayilar[0]; fBekliyor = true; } break; case AptTip::Rapid: rapid = true; break; case AptTip::Coolnt: satir(secenekVar(k, "OFF") ? "M09" : "M08"); break; case AptTip::Goto: { const int g = rapid ? 0 : 1; std::string kelimeler; const auto ekle = [&](const std::string& w) { if (!kelimeler.empty()) kelimeler += ' '; kelimeler += w; }; if (g != sonG) ekle(g == 0 ? "G00" : "G01"); static const char eksenAdi[3] = {'X', 'Y', 'Z'}; for (std::size_t e = 0; e < 3 && e < k.sayilar.size(); ++e) { const double v = k.sayilar[e]; if (!son[e] || *son[e] != v) { ekle(std::string(1, eksenAdi[e]) + koordinat(v)); son[e] = v; } } if (g == 1 && f && (fBekliyor || !fYazildi)) { ekle("F" + sayiFS(*f)); fBekliyor = false; fYazildi = true; } if (!kelimeler.empty()) satir(kelimeler); sonG = g; rapid = false; break; } case AptTip::End: satir("M05"); break; case AptTip::Fini: satir("M30"); break; case AptTip::Yorum: case AptTip::Diger: break; } } cikti += "%" + bicim_.satirSonu; return cikti; } } // namespace fikatia::post