KABAFORCEGeliştirici KabaOS'u edinin
Önizleme · 0.6.0 — dil 1.0 öncesidir; sözdizimi ve kütüphane değişebilir. Buradaki her örnek derlenerek sınanır.

8. Standart kütüphane turu

Modüller modul.fonksiyon() biçiminde çağrılır. Çoğu için kullan yazmanız gerekmez; yazmak okunurluğu artırır ve hangi yeteneği kullandığınızı belgeler.

KabaLang
kullan kaba.sistem            # sistem, zaman, dosya, matematik... birlikte gelir
kullan kaba.web               # HTTP sunucusu (yalnız gerektiğinde)
kullan kaba.arayuz            # pencereli uygulamalar

yazdir("modüller hazır")
Modül Ne için Durum
sistem makine adı, çekirdek, CPU, komut çalıştırma çalışıyor
dosya okuma/yazma/listeleme/kopyalama çalışıyor
zaman saat, tarih, biçimlendirme, süre ölçümü çalışıyor
matematik karekök, üs, yuvarlama, trigonometri çalışıyor
konsol girdi okuma, etkileşim algılama çalışıyor (belki/sonuc döner)
gunluk sistem günlüğüne düzeyli kayıt çalışıyor
cevre ortam değişkenleri çalışıyor
rastgele rastgele sayı/seçim/UUID çalışıyor
json JSON üretme/çözme/doğrulama çalışıyor
kripto sha256/sha512/md5/hmac/base64 çalışıyor
web HTTP sunucusu (12. sayfa) çalışıyor, ek kütüphane gerektirmez
servis daemon yaşam döngüsü (11. sayfa) çalışıyor
arayuz pencereli uygulama (10. sayfa) çalışıyor
ag HTTP istemcisi, indirme yalnız istemci; hata bildirimi sessiz
paket KabaOS paket işlemleri KabaOS'a özgü
veritabani SQLite sorgusu + parametre bağlama (libsqlite3) çalışıyor; SurrealDB tarafı deneysel
ses · ai bip/çalma, model sorgusu kısmi/deneysel

⚠️ "Sessiz hata bildirimi" ne demek: bu modüllerin bir kısmı başarısızlığı sonuc ile bildirmez, boş değer döndürür. Dönüşü elle denetleyin.

sistem

KabaLang
kullan kaba.sistem

yazdir("makine  : " + sistem.makine_adi())
yazdir("çekirdek: " + sistem.cekirdek())
yazdir("işlemci : " + sistem.cpu_model())
yazdir("kullanıcı: " + sistem.kullanici())

# Komut çalıştırıp ÇIKTISINI almak:
cikti = sistem.komut("echo merhaba").kirp()
dogrula(cikti == "merhaba", "sistem.komut çıktıyı verir")

# Yalnız çıkış kodu gerekiyorsa:
dogrula(sistem.calistir("true") == 0, "sistem.calistir çıkış kodu verir")

# ⚠️ DIŞARIDAN GELEN VERİ VARSA KABUKSUZ ÇAĞIR.
# `sistem.komut("rm " + girdi)` bir KABUK satırı kurar: girdi ";" içeriyorsa
# ikinci bir komut çalışır. Argümanları ayrı ayrı vermek bunu imkânsız kılar.
# ⚠️ YENİ AD: `cikti` yukarıda kullanıldı. Çoklu atama yeni adlar
#    bildirir; var olan bir adı YENİDEN BİLDİREMEZ.
argv_cikti, argv_kod = sistem.komut_argv(["echo", "; bu komut DEĞİL argüman"])
dogrula(argv_kod == 0, "komut_argv çıkış kodunu da verir")
dogrula(argv_cikti == "; bu komut DEĞİL argüman", "argüman kabuktan geçmez")

# Çıktı gerekmiyorsa:
dogrula(sistem.calistir_argv(["true"]) == 0, "calistir_argv")

# ⚠️ Çıkış kodu anlamları: 0.. programın kendi kodu · -1 sinyal ·
#    -2 program BULUNAMADI · -3 zaman aşımı. "Boş çıktı" ile "komut yok"
#    ayrı şeylerdir; `komut()` bu ayrımı veremez.
_, yokkod = sistem.komut_argv(["bulunmayan_ikili_xyz"])
dogrula(yokkod == -2, "bulunamayan program -2 döner")

# Programın kendi argümanları (ilki programın yoludur):
a = sistem.argumanlar()
dogrula(a.uzunluk() >= 1, "argümanlar")

yazdir("sistem tamam")

dosya

KabaLang
kullan kaba.sistem

dosya.klasor_olustur("deneme")
dosya.yaz("deneme/not.txt", "birinci satır\n")
dosya.ekle("deneme/not.txt", "ikinci satır\n")

dogrula(dosya.var_mi("deneme/not.txt"), "var_mi")
dogrula(dosya.dosya_mi("deneme/not.txt"), "dosya_mi")
dogrula(dosya.dizin_mi("deneme"), "dizin_mi")
dogrula(dosya.oku("deneme/not.txt").icerir("ikinci"), "oku")
dogrula(dosya.listele("deneme").uzunluk() == 1, "listele")
dogrula(dosya.boyut("deneme/not.txt") > 0, "boyut")

dosya.kopyala("deneme/not.txt", "deneme/kopya.txt")
dogrula(dosya.var_mi("deneme/kopya.txt"), "kopyala")

dosya.sil("deneme/not.txt")
dosya.sil("deneme/kopya.txt")
dosya.klasor_sil("deneme")
dogrula(degil dosya.var_mi("deneme"), "klasor_sil")

yazdir("dosya tamam")

zaman

KabaLang
kullan kaba.sistem

yazdir("saat : " + zaman.saat())          # 14:05:09
yazdir("tarih: " + zaman.tarih())         # 2026-09-18
yazdir("tam  : " + zaman.simdi())         # 2026-09-18 14:05:09
yazdir("yıl  : " + zaman.bicimlendir("%Y"))

dogrula(zaman.milisaniye() > 0, "milisaniye — dönemden beri")
dogrula(zaman.saniye() > 0, "saniye")
yazdir("zaman tamam")

matematik

KabaLang
kullan kaba.sistem

dogrula(matematik.karekok(9.0) == 3.0, "karekok")
dogrula(matematik.mutlak(-3) == 3, "mutlak")
dogrula(matematik.taban(3.7) == 3.0, "taban")
dogrula(matematik.tavan(3.2) == 4.0, "tavan")
dogrula(matematik.yuvarla(3.5) == 4.0, "yuvarla")
dogrula(sayi(matematik.tavan(3.2)) == 4, "tam sayıya çevirme")
dogrula(matematik.us(2.0, 10.0) == 1024.0, "us")
dogrula(matematik.pi > 3.14 ve matematik.pi < 3.15, "pi sabiti")

yazdir("matematik tamam")

taban, tavan ve yuvarla sonucu ondalik verir (matematik.taban(3.7) → 3.0; iki arka uçta aynı). Tam sayı gerekiyorsa sayi(...) ile çevirin.

rastgele

KabaLang
kullan kaba.sistem

z = rastgele.sayi(1, 6)
dogrula(z >= 1 ve z <= 6, "aralıkta rastgele sayı")
yazdir(f"zar: {z}")
yazdir("uuid: " + rastgele.uuid())

cevre — ortam değişkenleri

KabaLang
kullan kaba.sistem

kip = cevre.al("KABA_KIP")
eger kip == "":                 # tanımsızsa boş metin döner
    kip = "gelistirme"

yazdir(f"çalışma kipi: {kip}")

⚠️ Tanımsız değişken boş metin verir, hata vermez. "Tanımlı mı" ayrımı gerekiyorsa cevre.var_mi("AD") kullanın.

json

KabaLang
kullan kaba.sistem

metni = json.uret({"ad": "Deniz", "yas": 30})
yazdir(metni)
dogrula(json.gecerli_mi(metni), "gecerli_mi")
dogrula(json.al(metni, "ad") == "Deniz", "al — tek anahtar oku")

s = json.coz(metni)
dogrula(s["yas"] == 30, "coz — sözlüğe çevir")

yazdir(json.bicimlendir(metni))          # girintili
dogrula(degil json.gecerli_mi("{bozuk"), "bozuk JSON yakalanır")

yazdir("json tamam")

kripto

KabaLang
kullan kaba.sistem

ozet = kripto.sha256("merhaba")
dogrula(ozet.uzunluk() == 64, "sha256 onaltılık 64 karakter")
yazdir("sha256: " + ozet)

k = kripto.base64_kodla("merhaba")
dogrula(kripto.base64_coz(k) == "merhaba", "base64 gidiş-dönüş")
yazdir("kripto tamam")

gunluk — sistem günlüğü

yazdir kullanıcıya konuşur, gunluk işletim sistemine. Servisler için doğru olan budur: çıktı systemd günlüğüne düzeyiyle birlikte düşer.

KabaLang
kullan kaba.sistem

gunluk.bilgi("işlem başladı")
gunluk.uyari("disk %85 dolu")
gunluk.hata("bağlantı kurulamadı")

yazdir("günlük tamam")
Çağrı Düzey
gunluk.ayikla(m) hata ayıklama
gunluk.bilgi(m) bilgi
gunluk.dikkat(m) dikkat
gunluk.uyari(m) uyarı
gunluk.hata(m) hata
gunluk.kritik(m) kritik

desen — düzenli ifade (regex)

KabaLang
kullan kaba.desen

# Var mı? (metnin HERHANGİ BİR yerinde arar — baştan sona değil)
dogrula(desen.esler_mi("^ka.a", "kabalang"), "esler_mi")
dogrula(desen.esler_mi("^zz", "kabalang") == yanlis, "eşleşmeyince yanlış")

# İlk eşleşme
dogrula(desen.bul("[0-9]+", "sürüm 42 ve 7") == "42", "bul")

# Hepsi
dogrula(desen.tum_bul("[0-9]+", "1 ve 22 ve 333").uzunluk() == 3, "tum_bul")

# Yakalama grupları (grup 0 = tüm eşleşme LİSTEYE KONMAZ, onu bul() verir)
g = desen.gruplar("([a-z]+)@([a-z.]+)", "posta: ali@kaba.os")
dogrula(g[0] == "ali", "grup 1")
dogrula(g[1] == "kaba.os", "grup 2")

# Değiştirme — $1, $2 geri başvurudur
dogrula(desen.degistir("\\s+", "a   b     c", "-") == "a-b-c", "degistir")
dogrula(desen.degistir_ilk("a", "aaa", "X") == "Xaa", "yalnız ilki")

# Bölme — baştaki VE sondaki boş parça KORUNUR (Python re.split gibi)
dogrula(desen.bol(",\\s*", "a, b,c").uzunluk() == 3, "bol")
uclu = desen.bol(",", ",a,")
dogrula(uclu.uzunluk() == 3, "başta ve sonda boş parça korunur")
dogrula(uclu[0] == "" ve uclu[2] == "", "iki uçtaki boşluk atılmaz")

yazdir("desen tamam")

⚠️ Ters eğik çizgi İKİLENİR. Metin sabitinde "\\s+" yazılır; "\s+" yazan kişi s+ deseni elde eder ve desen sessizce yanlış çalışır. En sık düşülen tuzak budur.

⚠️ [a-z] Türkçe harfleri İÇERMEZ. ç ğ ı ö ş ü o aralığın dışındadır; "küçük harf" demek isteyen [a-zçğıöşü] yazmalıdır. Aynısı [A-Z] için de geçerlidir. Buna karşılık . bir KOD NOKTASI eşler, bayt değil:

KabaLang
kullan kaba.desen

# Tek bir `.` bütün bir Türkçe harfi alır — yarım karakter ÇIKMAZ.
dogrula(desen.bul("^.", "şeker") == "ş", "nokta kod noktası eşler")
dogrula(desen.bul("^..", "şeker") == "şe", "iki nokta iki harf")

yazdir("türkçe tamam")

⚠️ Kullanıcı verisini desene gömüyorsan kacir() kullan. Ham veri desene girerse en hafifi yanlış sonuç, en ağırı (a+)+$ gibi bir desenle üstel geri izlemedir (ReDoS).

KabaLang
kullan kaba.desen

aranan = "a.c"                      # kullanıcıdan geldi varsayalım
dogrula(desen.esler_mi(desen.kacir(aranan), "abc") == yanlis, "nokta artık joker değil")
dogrula(desen.esler_mi(desen.kacir(aranan), "a.c"), "gerçek metin eşleşir")

yazdir("kaçırma tamam")

ag — HTTP istemcisi

Ağ örneği bu belgede derlenir ama çalıştırılmaz (test makinesi internete çıkmak zorunda olmasın diye):

KabaLang
kullan kaba.ag

eger ag.baglanti_var_mi():
    icerik = ag.get("https://example.com")
    yazdir("gelen bayt: " + metin(icerik.uzunluk()))
degilse:
    yazdir("bağlantı yok")

Ulaşılamayan adres ya da HTTP hatası hata fırlatır (içerik gibi bir metin dönmez); dene/yakala ile yakalayın. Ağa gerçekten gidip gelen sınama, internete çıkmadan yerel bir sunucuyla cekirdek/test_ag_ai.py içinde koşar.

⚠️ kaba.ag bugün yalnız istemcidir. Sunucu yazmak için kaba.web kullanın (12. sayfa).

arsiv — tar · gzip · zstd

.kaba paketleri gzip'li tar'dır; üst-akım paketleri zstd. İkisi de aynı çağrılardan geçer — biçim içerikten algılanır, uzantıdan değil.

KabaLang
kullan kaba.arsiv

fonksiyon ana():
    # Oluşturma — biçim: "gzip" · "zstd" · "yok"
    dosyalar = ["KABAINFO", "usr/bin/program"]
    arsiv.olustur("paket.kaba", dosyalar, "gzip")

    # Üyeleri listele
    yazdir(metin(arsiv.listele("paket.kaba")))

    # Tek üyeyi OKU (metin üyeler için: KABAINFO, .PKGINFO …)
    bilgi = arsiv.uye_oku("paket.kaba", "KABAINFO")
    yazdir(bilgi)

    # "boş üye" ile "üye yok" ayrımı
    eger arsiv.uye_var_mi("paket.kaba", "KABAINFO"):
        yazdir("üstveri var")

    # Diske aç
    arsiv.ac("paket.kaba", "/tmp/hedef")
ana()

⚠️ ac() yol geçişine karşı kapalıdır. İçinde ../../etc/passwd taşıyan bir arşiv hedef dizinin dışına yazamaz; böyle bir arşiv reddedilir (yanlis döner). Paket açan bir araçta bu koruma olmadan zararlı bir paket doğrudan kök ele geçirir.

⚠️ uye_oku METİN döner. İkili üye içeriği için ac() kullanılır — dosya diske açılır. Dilde bayt türü yoktur; müşterisi yokken icat etmemek için.

⚠️ olustur DOSYA LİSTESİ alır, dizin değil. Dizin yürümek ayrı bir karardır (gizli dosyalar? bağlar? dışlamalar?) ve dosya.listele zaten var.

⚠️ libarchive yalnız bu modülü kullanan programa bağlanır. kullan kaba.arsiv yazmayan bir ikili ona hiç bağlanmaz (ölçülüyor).

toml — yapılandırma dosyası okuma

TOML, KabaOS'un yapılandırma yazımıdır: tasarım jetonları, paket üstverisi. kaba.toml onun belgelenmiş bir alt kümesini okur.

KabaLang
kullan kaba.toml
kullan kaba.dosya

fonksiyon ana():
    k = dosya.oku("kabaos-tasarim.toml")

    # Değer — yol NOKTALIDIR, `json.al` ile aynı düşünce
    yazdir(toml.al(k, "renk.vurgu.deger"))

    # "boş değer" ile "anahtar yok" AYRI ŞEYLER
    eger toml.var_mi(k, "renk.marka.deger"):
        yazdir("marka rengi tanımlı")

    # Dizi
    dongu b icinde toml.liste(k, "yukseklik.boylar"):
        yazdir(b)

    # Bir tablonun doğrudan alt anahtarları
    dongu ad icinde toml.anahtarlar(k, "renk"):
        yazdir(ad + " = " + toml.al(k, "renk." + ad + ".deger"))
ana()

⚠️ Tırnak içindeki # yorum değildir. renk = "#FF7A3D" satırını renk = " yapan bir ayrıştırıcı, hex renklerle dolu bir tasarım dosyasında her değeri bozar. Burada yorum ayıklama tırnağı bilir.

⚠️ Tırnaklı tablo başlığının tırnağı adın parçası değildir. [sema.plasma."Colors:Button"] başlığının yolu sema.plasma.Colors:Button olur. Tırnağı saklayan bir uygulamada o anahtar hiçbir zaman bulunamaz — hata da vermez, sadece yok görünür.

⚠️ Aynı adlı tablo dizisi ([[ad]]) ezilmez. Düz bir ad→değer haritasında ikinci [[ayrisma]] birinciyi silerdi. Yola sıra numarası eklenir: ayrisma.0.ad, ayrisma.1.ad … Tek [tablo] numara almaz.

⚠️ Desteklenmeyen sözdizimi SESSİZCE ATLANMAZ, satır numarasıyla hata verir ve dene/yakala ile yakalanabilir. Alt kümenin dışında kalanlar: satır içi tablo ({ }), üç tırnaklı ham metin ('''), tarih/saat türleri. Atlayan bir ayrıştırıcı, eksik jetonlu bir temayı "başarıyla derlendi" diye geçirirdi.

KabaLang
kullan kaba.toml

fonksiyon ana():
    dene:
        toml.al("a = { x = 1 }", "a")
    yakala h:
        yazdir(metin(h))          # toml: satır 1: satır içi tablo ...
ana()

⚠️ Değerler metin döner, sayıya çevrilmez. 0.90 kaynakta nasılsa öyle gelir; sayi() ile çevirirsiniz. Ayrıştırıcının kendi başına float'a çevirip geri basması, jeton üreten bir araçta 0.90ı 0.9 yapardı.

dbus — masaüstü ve sistem servisleriyle konuşma

KabaOS'un parçaları birbirine D-Bus'la konuşur: KWin, Plasma, systemd. kaba.dbus bunu dış süreç çağırmadan yapar.

KabaLang
kullan kaba.dbus

fonksiyon ana():
    # Argümansız çağrı
    dbus.cagir("org.kde.KWin", "/KWin", "org.kde.KWin", "reconfigure")

    # Argümanlı çağrı — imza AÇIKÇA verilir ("u" = uint32)
    dbus.cagir("org.kde.KWin", "/KWin", "org.kde.KWin",
               "setCurrentDesktop", "u", ["2"])

    # Dizi dönen çağrı
    dongu ad icinde dbus.cagir_liste("org.freedesktop.DBus",
                                     "/org/freedesktop/DBus",
                                     "org.freedesktop.DBus",
                                     "ListNames", "", []):
        yazdir(ad)

    # Servis otobüste var mı?
    eger dbus.hizmet_var_mi("org.kde.KWin"):
        yazdir("KWin çalışıyor")

    # Özellik oku / yaz
    yazdir(dbus.ozellik_al("org.kde.KWin", "/KWin", "org.kde.KWin", "id"))
ana()

⚠️ İmza tahmin edilmez, sizden istenir. D-Bus güçlü tiplidir: setCurrentDesktop u (uint32) bekler, i göndermek hata verir. qdbus bunu her çağrıda introspection ile tahmin eder; biz etmiyoruz — tahmin yanlış tipi sessizce seçebilir ve her çağrıya bir gidiş-dönüş + XML ayrıştırma ekler. Harfler: y n q i u x t d b s o g.

⚠️ Sayıya çevrilemeyen argüman sessizce 0 olmaz. "iki" yazarsanız hata alırsınız. atoi sessizce 0 döndürseydi çağrı masaüstü 0'a giderdi ve kimse fark etmezdi.

⚠️ Hata sessiz değildir. D-Bus hatası fırlar ve dene/yakala ile yakalanır; iletide hatanın D-Bus adı geçer. Yerine geçtiği kabuk deseni (qdbus6 … 2>/dev/null) tam tersini yapıyordu: hata kaybolur, betik başarılı sanılırdı.

⚠️ Sinyal aboneliği YOKTUR. Ölçüm: KabaOS'ta yöntem çağrısı 49, Properties.Set 7, Properties.Get 1, sinyal aboneliği 0. Sinyal bir olay döngüsü ister ve dilin eşzamanlılık sözlüğü birlikte/kanaltır.

⚠️ Oturum otobüsü varsayılan, sistem otobüsü için dbus.sistem_cagir.

⚠️ libsystemd yalnız bu modülü kullanan programa bağlanır (ölçülüyor — ldd ile değil objdump -p DT_NEEDED ile: Qt6Core libsystemd'yi zaten dolaylı çekiyor).

sinama — sınama iddiaları

KabaLang
kullan kaba.sinama

fonksiyon ana():
    sinama.esit(2 + 2, 4, "toplama")
    sinama.esit_degil("a", "b", "farklı")
    sinama.dogru(1 < 2, "küçüktür")
    sinama.yanlis(1 > 2, "büyük değil")
    sinama.yaklasik(0.1 + 0.2, 0.3, 0.000001, "ondalık")
    sinama.icerir("merhaba dünya", "dünya", "içerme")
    sinama.ozet()
ana()

⚠️ Kusurlar birikir, ilk kusurda durmaz. Eski dogrula başarısızlıkta exit(1) çağırıyordu: 50 iddialı bir dosyada 1. iddia düşerse 2–50 arası hiç koşmuyor, kaç şeyin bozuk olduğunu öğrenmek için 50 kez derle-çalıştır turu gerekiyordu.

⚠️ Beklenen ve bulunan yazılır. dogrula(a == b, "esit") düştüğünde ekranda yalnız "esit" vardı; a ile b görünmüyordu.

⚠️ Sessizce geçmek imkânsızdır — iki kapı birden. ozet() çağrılmadan biten kusurlu bir koşu yine de 1 ile çıkar; ve hiç iddia koşmayan bir dosya da kusurludur. kullan kaba.sinama yazıp hiçbir şey sınamamak, koşucuya yalan söylemektir.

⚠️ Ondalık sayıda esit değil yaklasik. 0.1 + 0.2 == 0.3 yanlıştır. Tolerans zorunludur: varsayılan bir tolerans vermek, hassasiyeti hiç düşünmemeye yol açardı.

⚠️ dogrula kaldırılmadı. 230 kullanımı var ve "ilk kusurda dur" davranışı bazı yerlerde bilinçli. sinama.* onun yanına gelir.

belge — docx · xlsx · pdf üretimi

Rapor, teklif, sertifika. Office eklentisi değil, belge ÜRETİCİSİ: program belgeyi kendi yazar, Word/Excel/LibreOffice okur.

KabaLang
kullan kaba.belge

fonksiyon ana():
    b = belge.yeni("Bilirkişi Raporu")        # yalnız ÜSTVERİ (dc:title)
    belge.altbilgi(b, "ForensicAI · GİZLİ")   # + "Sayfa X / Y" kendiliğinden

    belge.baslik(b, "ADLİ BİLİMLER RAPORU", 1)
    belge.paragraf(b, "Dava No: 2026/1487")
    belge.vurgu(b, "GİZLİ — yalnızca yetkili kişiler")

    belge.baslik(b, "DELİL LİSTESİ", 2)
    belge.tablo_basligi(b, ["No", "Tür", "Açıklama"])
    belge.tablo_satiri(b, ["D-1", "Biyolojik", "Kan lekesi"])
    belge.tablo_satiri(b, ["D-2", "Dijital", "Disk imajı"])

    belge.sayfa_sonu(b)
    belge.baslik(b, "İMZA VE ONAY", 2)
    belge.paragraf(b, "Hazırlayan: ForensicAI")

    belge.docx_yaz(b, "rapor.docx")       # Word belgesi
    belge.xlsx_yaz(b, "rapor.xlsx")       # tablolar → çalışma sayfaları
    belge.kapat(b)
ana()

pdf_yaz — aynı belge, kendi dizgisiyle

Aynı belgeden PDF de çıkar. Ek bir çağrı yeter:

KabaLang
kullan kaba.belge

fonksiyon ana():
    b = belge.yeni("Bilirkişi Raporu")
    belge.altbilgi(b, "ForensicAI · GİZLİ")
    belge.baslik(b, "DELİL LİSTESİ", 2)
    belge.tablo_basligi(b, ["No", "Tür", "Açıklama"])
    belge.tablo_satiri(b, ["D-1", "Biyolojik", "Kan lekesi"])
    belge.paragraf(b, "DELİL LİSTESİ ğüşıöçĞÜŞİÖÇ")

    eger belge.pdf_yaz(b, "rapor.pdf"):
        yazdir("PDF yazıldı")
    degilse:
        yazdir("PDF yazılamadı: yazı tipi yok ya da gömülemeyen karakter")
    belge.kapat(b)
ana()

PDF çıktısı hakkında bilmeniz gerekenler:

Konu Davranış
Bağımlılık PDF üretmek ek kütüphane gerektirmez; PDF üreten programın bağımlılıkları, yalnız docx üretenle aynıdır (ölçüldü).
Türkçe karakterler PDF'in standart yazı tipleri Türkçe taşımaz (DELİL LİSTESİ → DEL?L L?STES?). Bu yüzden yazı tipi belgeye gömülür; metin pdftotext gibi araçlarla birebir geri okunur (ölçüldü).
Dizgi Satır sarma · sayfa sonu · sayfa numarası · sütun genişliği · tablo başlığının her sayfada tekrarı kendiliğinden yapılır.

⚠️ Yazı tipi DejaVu Sans, ve seçim ölçüldü. Türkçe harflerin tamamını ve ₺yi taşıyor, statik glyf tabanlı (değişken yazı tipinin alt kümesi için örnekleme gerekirdi), fsType = 0 yani gömme kısıtsız, lisansı gömmeye açık. Adaylardan Liberation Sans ve Carlito'da ₺ yok; Noto Sans ve Carlito ISO paket listesinde yok; Adwaita Sans değişken yazı tipi. Paketi (ttf-dejavu) KABASPEC'in depends= satırında yazılıdır — "zaten kurulu" bir varsayımdır, bağımlılık bir sözleşmedir.

⚠️ Yalnız kullanılan glifler gömülür. Ölçüldü: örnek raporda iki yazı tipi toplam ~21 KB; tam gömme 1,4 MB olurdu. Belgede hiç kalın metin yoksa ikinci yazı tipi hiç gömülmez.

⚠️ pdf_yaz gömülemeyen bir karakter görürse yanlis döner ve dosya YAZMAZ. Ölçülen örnek: ₼ (U+20BC) DejaVu'da yok. Sessizce atlamak "1.500 ₼" tutarını "1.500 " yapardı; bilirkişi raporunda bu, yanlış çıktıdır. Aynı şey yazı tipi paketi kurulu değilse de olur — yarım dosya kalmaz, çünkü dosya ancak baytlar hazır olduktan sonra açılır.

⚠️ Sayfa düzeni docx ile AYNI sayılardır: A4, 2 cm kenar, gövde 11 pt, başlık 16/14/12 pt. İki çıktının aynı kaynaktan farklı sayfalanması bir kusur olurdu. Fark şurada: docx'te sayfa numarası bir alan kodudur (okuyucu hesaplar), PDF'te biz hesaplayıp basarız.

⚠️ Satırlar sola dayalıdır; iki yana yayma ve heceleme yok (müşterisi ölçülmedi). Akış sıkıştırma da yok: zlib yeni bir sözleşme olurdu ve ölçülen kazanç bunu karşılamıyor — yukarıdaki örnek rapor 27 KB, 21,5 KB'ı gömülü yazı tipi alt kümeleri.

⚠️ Tablo DÜZ LİSTE alır, iç içe liste değil. tablo_basligi tabloyu açar ve sütun sayısını belirler; tablo_satiri ona satır ekler. Tablo dışı ilk öğe tabloyu örtük olarak kapatır. (Bu API iç içe liste dilde yokken tasarlandı; iç içe liste artık destekleniyor — bkz. 4. sayfa — ama tablo API'si satır satır kalır.)

⚠️ Başlıksız satır belgeye GİRMEZ (yanlis döner): sütun sayısı bilinmeden yazılan satır tabloyu sessizce bozardı. Eksik hücreli satır ise doldurulur, fazlası kırpılır.

⚠️ Tablosuz belgede xlsx_yaz yanlis döner ve dosya yazmaz. Sıfır sayfalı bir çalışma kitabı Excel'in açamadığı bir dosyadır; çağıran "yazdım" sanırdı.

⚠️ xlsx sayfa adı tablodan önceki BAŞLIKTAN gelir, 31 karakterde kırpılır (bayttan değil — 31. bayttan kesmek Öyü ikiye böler ve dosya hiç açılmaz) ve çakışırsa numaralanır.

⚠️ Sayı tanıma DAR tutuldu. 12.5 sayı, 007 metin kalır: baştaki sıfırı yutan bir dışa aktarma fatura numarasını bozar. 1.234,56 da metindir — ondalık ayracını tahmin etmek tutarı 1000 katına çıkarabilir.

⚠️ Aynı girdi → aynı bayt. Paketin zaman damgası sabittir; aynı delilden üretilen rapor, üretim saatine göre farklı özet vermez (imzalanan rapor için şart).

⚠️ libarchive yalnız bu modülü (ya da kaba.arsivi) kullanan programa bağlanır — docx/xlsx birer ZIP'tir ve ZIP tabanı kaba.arsivindir; ikinci bir ZIP yazıcısı yazılmadı.

UYGULANMADI: görsel gömme (üç tüketicide de sıfır kullanım) · satır içi renk · üstbilgi · içindekiler · dipnot · hücre birleştirme · xlsx formülü · PDF'te eğik yazı, iki yana yayma ve akış sıkıştırma.

veritabani — SQLite

Sorgu libsqlite3'e gider; kabuk yoktur, sqlite3 ikilisi çağrılmaz. Dönüş biçimi: sütunlar |, satırlar satır sonu ile ayrılır, NULL boş dizedir, sonu kırpılmıştır.

KabaLang
kullan kaba.veritabani

db = "ornek.db"
dosya.sil(db)

# Şema ve veri — çoklu deyim tek çağrıda verilebilir
veritabani.sqlite(db, "CREATE TABLE kisi(id INTEGER, ad TEXT);")
veritabani.sqlite(db, "INSERT INTO kisi VALUES (1,'Ayşe'),(2,'Ali'),(3,'O''Brien');")

hepsi = veritabani.sqlite(db, "SELECT id, ad FROM kisi ORDER BY id")
yazdir(hepsi)
dogrula(hepsi == "1|Ayşe\n2|Ali\n3|O'Brien", "sütun | ile, satır \\n ile")

# ⚠️ KULLANICI VERİSİ VARSA PARAMETRE BAĞLA — üçüncü argüman bir listedir
ad = "O'Brien"
bulunan = veritabani.sqlite(db, "SELECT id FROM kisi WHERE ad = ?", [ad])
dogrula(bulunan == "3", "tek tırnaklı değer kaçış yazmadan bağlanır")

# Enjeksiyon denemesi parametre olarak geçtiğinde yalnız VERİdir
kotu = veritabani.sqlite(db, "SELECT ad FROM kisi WHERE ad = ?",
                         ["Ali'; DROP TABLE kisi; --"])
dogrula(kotu == "", "eşleşen kayıt yok — dize bir deyim değil")
dogrula(veritabani.sqlite(db, "SELECT count(*) FROM kisi") == "3",
        "tablo duruyor, DROP çalışmadı")

dosya.sil(db)

⚠️ Metin birleştirerek sorgu kurmayın. İki yüzey arasındaki fark güvenliktir, üslup değil — ve fark aşağıda ÖLÇÜLÜYOR, anlatılmıyor:

KabaLang
kullan kaba.veritabani

db = "enjeksiyon.db"
dosya.sil(db)
veritabani.sqlite(db, "CREATE TABLE kisi(ad TEXT);")
veritabani.sqlite(db, "INSERT INTO kisi VALUES ('Ayşe'),('Ali'),('Veli');")

girdi = "yok' OR '1'='1"

# ⛔ BİRLEŞTİRME: girdi sorgunun ANLAMINI değiştirir — 3 kayıt döner
acik = veritabani.sqlite(db, "SELECT ad FROM kisi WHERE ad = '" + girdi + "'")
dogrula(acik.bol("\n").uzunluk() == 3, "birleştirme tüm tabloyu sızdırdı")

# ✓ PARAMETRE: aynı girdi yalnız bir DEĞERdir — eşleşen kayıt yok
kapali = veritabani.sqlite(db, "SELECT ad FROM kisi WHERE ad = ?", [girdi])
dogrula(kapali == "", "parametre bağlanınca girdi kod değil veri")

dosya.sil(db)
yazdir("ölçüldü: birleştirme sızdırıyor, parametre sızdırmıyor")

⚠️ Parametreli çağrı TEK deyim kabul eder. "SELECT 1; DROP TABLE x;" reddedilir — yığılmış sorgu (stacked query) yolu kapalıdır. Parametre sayısı da denetlenir: eksik bağlanan bir ? SQLite'ta sessizce NULL olur ve sorgu "kayıt yok" dönerdi.

⚠️ Hata dönüş değerinde DEĞİL, stderr'dedir. Başarısız sorgu boş dize döner ve sebebi ✗ sqlite: ... satırıyla yazılır. Dönüşü elle denetleyin.

⚠️ Yol alanı URI olamaz. "file:veri.db?mode=ro" reddedilir: yol alanının bir sorgu dizesi ayrıştırıcısını beslemesi, kapatılan açığın küçük bir kopyası olurdu. ":memory:" çalışır.

⚠️ Sorgunun 10 saniyelik bir sınırı var. Aşılırsa sorgu kesilir, o ana kadar biriken satırlar döner ve stderr'e zaman aşımı yazılır.

⚠️ libsqlite3 yalnız bu modülü kullanan programa bağlanır (ölçülüyor).

Sonraki adım

Uygulama türleri: 9. Konsol · 10. Arayüz · 11. Servis · 12. Web