Forum: D Programlama Dili RSS
Web sayfasından uygulama çalıştırmak
Sayfa:  1  2  sonraki 
zafer #1
Üye Tem 2009 tarihinden beri · 695 mesaj · Konum: Ankara
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: Web sayfasından uygulama çalıştırmak
Merhaba,

Web sayfamda bulunan bir düğme ile bir konsol uygulaması başlatmak istiyorum. Bu işlem için std.process modülünde bulunan spawnProcess metodunu kullanıyorum.

Buradaki sorun, uygulama çalıştığı sürece web sayfam bloke oluyor. spawnProcess metodunun dönüş değerini kullanmazsam sayfa bloke olmuyor ancak bu seferde işlemin bittiğini bilemiyorum. Kısaca spawnProcess metodunu eşzamansız çalıştırmak mümkün mü?
https://github.com/zafer06 - depo
acehreli (Moderatör) #2
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4481 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
spawnProcess'in sonucunu wait() ile alıyorsan program bekliyormuş. O zaman bir seçenek, thread kullanmak:
import std.stdio;
import std.process;
import core.thread;
import std.concurrency;
 
void main() {
    // Bunların gelen isteklerle ilgili olduklarını varsayalım
    // (Anahtar istek id'si, değer de istenen işlem)
    immutable(string[])[int] istekler = [ 42 : [ "/bin/sleep", "3" ],
                                          99 : [ "/bin/sleep", "4" ],
                                          7[ "/bin/sleep", "2" ] ];
 
    // İstekleri burada baştan başlatıyoruz ama tabii ki ana döngü sırasında
    // ilgisiz zamanlarda da başlatılabilirler
    foreach (t; istekler.byKeyValue) {
        spawn(&işlemci, t.key, t.value);
    }
 
    // Programın ana döngüsü
    int toplamSonlanan;
    while (toplamSonlanan < istekler.length) {
        writeln("Ana döngü bekliyor...");
        Thread.sleep(500.msecs);
        receiveTimeout(0.msecs,
                       (int id, int sonuç) {
                           writefln("%s numaralı istek %s değeriyle sonlandı", id, sonuç);
                           ++toplamSonlanan;
                       });
    }
}
 
void işlemci(int id, immutable string[] işlem) {
    auto pid = spawnProcess(işlem);
    writefln("%s numaralı isteğin işlemi başladı: %s", id, işlem);
    auto sonuç = wait(pid);
    writefln("%s işlemi %s değeriyle sonlandı", pid, sonuç);
    ownerTid.send(id, sonuç);
}
Bir çıktısı:

Ana döngü bekliyor...
99 numaralı isteğin işlemi başladı: ["/bin/sleep", "4"]
42 numaralı isteğin işlemi başladı: ["/bin/sleep", "3"]
7 numaralı isteğin işlemi başladı: ["/bin/sleep", "2"]
Ana döngü bekliyor...
Ana döngü bekliyor...
Ana döngü bekliyor...
Ana döngü bekliyor...
std.process.Pid işlemi 0 değeriyle sonlandı
7 numaralı istek 0 değeriyle sonlandı
Ana döngü bekliyor...
Ana döngü bekliyor...
std.process.Pid işlemi 0 değeriyle sonlandı
42 numaralı istek 0 değeriyle sonlandı
Ana döngü bekliyor...
std.process.Pid işlemi 0 değeriyle sonlandı
99 numaralı istek 0 değeriyle sonlandı


Başka yöntem ise sonucu tryWait() ile beklemekmiş:
import std.stdio;
import std.process;
import core.thread;
import std.array;
import std.algorithm;
 
struct Beklenen {
    int id;
    Pid pid;
}
 
void main() {
    // Bunların gelen isteklerle ilgili olduklarını varsayalım
    // (Anahtar istek id'si, değer de istenen işlem)
    immutable(string[])[int] istekler = [ 42 : [ "/bin/sleep", "3" ],
                                          99 : [ "/bin/sleep", "4" ],
                                          7[ "/bin/sleep", "2" ] ];
 
    Beklenen[] beklenenler;
 
    // İstekleri burada baştan başlatıyoruz ama tabii ki ana döngü sırasında
    // ilgisiz zamanlarda da başlatılabilirler
    foreach (t; istekler.byKeyValue) {
        auto pid = spawnProcess(t.value);
        writefln("%s numaralı isteğin işlemi başladı: %s", t.key, t.value);
        beklenenler ~= Beklenen(t.key, pid);
    }
 
    // Programın ana döngüsü
    size_t adet;
    // Öylesine bir sonlanma koşulu
    while (adet < 10) {
        ++adet;
        writeln("Ana döngü bekliyor...");
        Thread.sleep(500.msecs);
 
        if (beklenenler.empty) {
            writeln("Beklenen işlem yok");
        } else {
            foreach (ref beklenen; beklenenler) {
                auto sonuç = tryWait(beklenen.pid);
                if (sonuç.terminated) {
                    writefln("%s numaralı istek %s değeriyle sonlandı", beklenen.id, sonuç.status);
                    // Daha sonradan silmek için işaretleyelim (döngüdeki ref'e dikkat)
                    beklenen.id = int.min;
                }
            }
            // Bunun için daha etkin yöntemler var. Burada çok basitçe:
            beklenenler = beklenenler.filter!(b => b.id != int.min).array;
        }
    }
}
Onun çıktısı:

7 numaralı isteğin işlemi başladı: ["/bin/sleep", "2"]
42 numaralı isteğin işlemi başladı: ["/bin/sleep", "3"]
99 numaralı isteğin işlemi başladı: ["/bin/sleep", "4"]
Ana döngü bekliyor...
Ana döngü bekliyor...
Ana döngü bekliyor...
Ana döngü bekliyor...
7 numaralı istek 0 değeriyle sonlandı
Ana döngü bekliyor...
Ana döngü bekliyor...
Ana döngü bekliyor...
42 numaralı istek 0 değeriyle sonlandı
Ana döngü bekliyor...
99 numaralı istek 0 değeriyle sonlandı
Ana döngü bekliyor...
Beklenen işlem yok
Ana döngü bekliyor...
Beklenen işlem yok


Ali
acehreli (Moderatör) #3
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4481 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yukarıda şöyle demiştim:
            // Bunun için daha etkin yöntemler var. Burada çok basitçe:
            beklenenler = beklenenler.filter!(b => b.id != int.min).array;
Diziden eleman çıkartmanın etkin bir yöntemi, std.algorithm.remove'u (program için kabul edilebiliyorsa SwapStrategy.unstable ile) kullanmaktır:
import std.random;
import std.range;
import std.algorithm;
import std.stdio;
 
auto rasgeleSayılar(size_t adet) {
    return iota(adet).map!(_ => uniform(0, 100));
}
 
void main() {
    auto a = rasgeleSayılar(20).array;
    writefln("Önce : %s", a);
 
    auto b = a.remove!(e => e % 2);
 
    // a'nın yerinde değiştiğini kanıtlayalım:
    assert(a.ptr == b.ptr);
 
    writefln("Sonra: %s", b);
}
assert'ün gösterdiği gibi, a yerinde değişmiştir. Yani, b yeni bir dizi değildir.

Önce : [91, 98, 89, 26, 85, 93, 9, 19, 99, 13, 82, 5, 36, 22, 51, 71, 48, 71, 37, 76]
Sonra: [98, 26, 82, 36, 22, 48, 76]

Eğer elemanların sıraları önemli değilse şu genelde daha hızlıdır:
    auto b = a.remove!(e => e % 2, SwapStrategy.unstable);
Dikkat ederseniz, çıkartılan 89'un yerine sondaki 46 gelmiş (böylece elemanların kopyalanmaları aza indirgenmiştir), vs.:

Önce : [89, 95, 83, 15, 70, 99, 23, 37, 57, 70, 39, 21, 29, 80, 98, 63, 79, 59, 48, 46]
Sonra: [46, 48, 98, 80, 70, 70]


Her iki yöntemde de a degişmiştir ve herhalde artık kullanışlı değildir. (a'nın son tarafındaki elemanların bazıları a'nın baş tarafındakilerle aynıdır.) Dolayısıyla, normalde sonuç doğrudan a'ya atanın (ben farkını gösterebilmek için b tanımladım):
    a = a.remove!(e => e % 2, SwapStrategy.unstable);

Şimdi bir sorun, a'ya yeni eleman eklemeye kalktığımızda ortaya çıkabilir çünkü druntime a'nın sondaki elemanlarının kimse tarafından kullanılmamakta olduğunu bilemez ve yeni eleman eklediğimizde bütün diziyi yeni bir yere taşır:
    writefln("Eleman eklemeden önce  : %s", a.ptr);
    a ~= 222222;
    writefln("Eleman ekledikten sonra: %s", a.ptr);
Elemanlar artık yeni bir yerdedir :(:

Eleman eklemeden önce  : 7FCC6E782080
Eleman ekledikten sonra: 7FCC6E784000

a'nın başka kimse tarafından kullanılmadığından eminsek assumeSafeAppend'i çağırabiliriz. assumeSafeAppend, dilimin kapasitesini yerinde arttırır. (Örneğin, kapasite en azından a'nın en baştaki uzunluğu kadar olacaktır.) (Ancak, bunu gösterebilmek için çok debelendim çünkü henüz giderilmemiş olan bir hata var: array() ile oluşturulan dilimlerin kapasiteleri hep 0 kalıyor. O yüzden örneği varolan bir dilimin sonuna eleman ekleyerek başlatacak biçimde değiştiriyorum.)
import std.random;
import std.range;
import std.algorithm;
import std.stdio;
 
auto rasgeleSayılar(size_t adet) {
    return iota(adet).map!(_ => uniform(0, 100));
}
 
void main() {
    // Değişiklik: .array çağırmak yerine bir diziye ekleyerek başlıyorum
    int[] a;
    rasgeleSayılar(20).each!(e => a ~= e);
    writefln("Önce : %s", a);
 
    a = a.remove!(e => e % 2, SwapStrategy.unstable);
    writefln("Sonra: %s", a);
 
    writefln("Eleman eklemeden önce  : %s (kapasite: %s)", a.ptr, a.capacity);
    assumeSafeAppend(a);
    a ~= 222222;
    writefln("Eleman ekledikten sonra: %s (kapasite: %s)", a.ptr, a.capacity);
}
Artık a'ya yeni eklenen eleman yerinde eklenmiştir çünkü assumeSafeAppend a'nın elde edebileceği bütün kapasiteyi ona vermiştir. (Dikkat: Artık işimize yaramayan a'nın sonundaki eski elemanlara eriştiren başka dilim bulunmadığını kendimiz sağlamalıyız.)

Eleman eklemeden önce  : 7F3B51E15080 (kapasite: 0)
Eleman ekledikten sonra: 7F3B51E15080 (kapasite: 31)

Tabii bütün bu işlemleri otomatik olarak halleden bir tür kullanmak isteyebiliriz:
import std.random;
import std.range;
import std.algorithm;
import std.stdio;
 
struct YerindeDizi(T) {
    T[] elemanlar;
 
    void ekle(T e) {
        elemanlar ~= e;
    }
 
    void sil(alias kıstas, SwapStrategy swapStrategy = SwapStrategy.unstable)() {
        elemanlar = elemanlar.remove!(kıstas, swapStrategy);
        elemanlar.assumeSafeAppend();
    }
 
    // Dikkat: 'elemanlar' üyesine doğrudan erişime izin verilmemelidir çünkü
    // bu türü kullananların ellerindeki dilimler assumeSafeAppend ile geçersiz
    // (veya kullanışsız) hale gelebilir.
    //
    // Ben burada daha fazlasını gerçekleştirmeyeceğim.
}
 
auto rasgeleSayılar(size_t adet) {
    return iota(adet).map!(_ => uniform(0, 100));
}
 
void main() {
    auto a = YerindeDizi!size_t();
 
    foreach (i; 0 .. 100) {
        const önceki_ptr = a.elemanlar.ptr;
        rasgeleSayılar(20).each!(e => a.elemanlar ~= e);
 
        // (Bunu bir isimsiz işlev (lambda) yerine static bir yerel işlev olarak
        // tanımlamak zorunda kaldım. dmd'nin bilinen bir yetersizliği...)
        static bool kıstas(size_t eleman) {
            return eleman % 2;
        }
 
        a.sil!(kıstas, SwapStrategy.unstable);
        if (a.elemanlar.ptr != önceki_ptr) {
            writefln("Yer değişti - ptr: %s, length: %s, capacity: %s",
                     a.elemanlar.ptr, a.elemanlar.length, a.elemanlar.capacity);
        }
    }
 
 
    writefln("Son         - ptr: %s, length: %s, capacity: %s",
             a.elemanlar.ptr, a.elemanlar.length, a.elemanlar.capacity);
}
Sonuçta, başta gösterdiğim 'beklenenler' gibi yalnızca bir kaç eleman taşıyacak olan diziler yerine buradaki gibi YerindeDizi gibi bir tür kullanılırsa elemanlar hiç kopyalanmazlar. Yukarıdaki programda 100 kere 20 eleman ekleyip tek sayı olanlarını çıkartıyorum. Dolayısıyla bu dizi gerçekçi olmayan biçimde hep büyüse de (ortalamada 1000 eleman içerir) elemanlar yalnızca 5 kere kopyalanmak zorunda kalıyorlar:

Yer değişti - ptr: 7F01B8778100, length: 7, capacity: 31
Yer değişti - ptr: 7F01B877E000, length: 26, capacity: 63
Yer değişti - ptr: 7F01B877F000, length: 59, capacity: 127
Yer değişti - ptr: 7F01B877C800, length: 122, capacity: 255
Yer değişti - ptr: 7F01B7879010, length: 255, capacity: 509
Son         - ptr: 7F01B7879010, length: 1007, capacity: 1021

Ali
zafer #4
Üye Tem 2009 tarihinden beri · 695 mesaj · Konum: Ankara
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Ali eline sağlık burada harika bilgiler ve güzel D olanaklarını örneklemişsin. Hepsini daha detaylı incelemek için sonraya bırakıyorum.

Thread fikri aklıma hiç gelmemişti. Sen bunu söyleyince Vibe.d üzerindeki Task olanağı aklıma geldi. Birde onunla deneme yapmayı düşünüyorum.

Ayrıca tetikleme işlemi web sayfası üzerinden olacak ve program çalışıyorsa ikinci bir örnek çalıştırılmayacak. Bu yüzden programın çalışıp çalışmadığınıda takip etmem gerekiyor.
https://github.com/zafer06 - depo
acehreli (Moderatör) #5
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4481 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
zafer:
program çalışıyorsa ikinci bir örnek çalıştırılmayacak. Bu yüzden programın çalışıp çalışmadığınıda takip etmem gerekiyor.

O zaman en iyisi hiç thread başlatmadan gereken zamanlarda tryWait'i çağırmak (örneğin, belirli aralıklarda veya birisi tekrar tıklandığında). tryWait'in dönüş değerinin .terminated niteliği önceki işlemin sonlanıp sonlanmadığını bildiriyor.

Ali
Avatar
Salih Dinçer #6
Üye Ock 2012 tarihinden beri · 1908 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Selamlar Zafer ve Ali hocam...

Çalıştığın projeyi hangi yapıda (server platform?) gerçekliyorsun? Sorumu sana yardımcı olabileceğimden değil de ilgimi çektiğinden sana yöneltmekteyim :)

Bu arada bunu yapabilmek için kiralık bir sunucuya ihtiyaç var değil mi? Yani D'de yazdığımız bir uygulama, CGI kullanılmadan çalıştırılıp yine sonuçları web interface ile ziyaretçiye gösterilmesi mümkün mü? Çok ilgimi çekti de bizzat basit bir örnek ile yardımcı olursan müteşekkir olurum.

Sevgiler, saygılar... (bu arada hoş bulduk :D)
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #7
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4481 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Örnek olarak vibe.d gibi çatılar var. Biz de şu konuda doğrudan socket üzerinden incelemiştik:

  http://ddili.org/forum/thread/1261

Sonuçta önemli olan, HTTP protokolünü uygulamak: İstek düzeni belli, yanıt düzeni belli. Yukarıdaki örnekte HTTP yanıtı şöyle oluşturulup gönderiliyor:
    enum başlık =
        "HTTP/1.0 200 OK\nContent-Type: text/html; charset=utf-8\n\n";
 
    string yanıt = başlık;
    yanıt ~= "<b>Merhaba Dünya!</b>\n";
 
    istemci.send(yanıt);
Ali
zafer #8
Üye Tem 2009 tarihinden beri · 695 mesaj · Konum: Ankara
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Selamlar Salih, seni gördüğüme sevindin :)

Ben projem için Vibe.d çatısını kullanıyorum. Web tabanlı uygulamalar geliştirmek için gerekli altyapı ve olanakları Vibe.d sana sunuyor. Bundan dolayı kendi bilgisayarında tüm çalışmanı yapabilirsin.

Proje aşamasında bir sunucuya ihtiyacın yok. Web tarafı yani talepler, cevaplar, rotalar gibi tüm alt düzey işleri Vibe.d çatısı hallediyor. Yani D ile web tabanlı bir uygulama geliştirip sonucu bir web arayüzü (tarayıcı) ile ziyaretçilere sunabilirsin.

Merak ediyorsan Vibe.d github sayfasında başlaman için gerekli bilgi var. Takıldığın yerde hep burada ve yanındayız :)
https://github.com/zafer06 - depo
Avatar
Salih Dinçer #9
Üye Ock 2012 tarihinden beri · 1908 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Ben bu çatıya hiç alışamadım. Kontrol bende değilmiş izlenimi veriyor. D'nin kendi olanaklarıyla hareket etmek isterdim.
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
zafer #10
Üye Tem 2009 tarihinden beri · 695 mesaj · Konum: Ankara
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Salih Dinçer:
D'nin kendi olanaklarıyla hareket etmek isterdim.

Ali'nin önceki mesajlarda eklediği örnek üzerinden gidebilirsin. Tamamen D olanaklarını kullanıyor. Ancak projen gelişmeye başladığı zaman bir sunucu ile entegrasyonu, rotalar, yetkilendirme, mime türleri ve güvenli bağlantı (ssh) gibi konuları kendi başına çözmeyi göze almalısın.

D ile web tarafında ilerlemek istiyorsan Vibe.d bence güzel bir çatı. Halen bir çok eksiği var. Yinede oldukça modern olanaklara ve sade ve temiz bir kodlama yapısına sahip. Bence D ile web tarafında çalışacaklar için iyi bir tercih olur. Performansıda biraz daha iyi olsaydı daha iyi olurdu :( https://www.techempower.com/benchmarks/#section=data-r14&…
https://github.com/zafer06 - depo
acehreli (Moderatör) #11
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4481 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Performans konusunu sordum:

  http://forum.dlang.org/post/oh8hun$1669$1@digitalmars.com

Ali
acehreli (Moderatör) #12
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4481 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Verilen yanıtlara göre kullananlar yine de vibe.d'yi çok hızlı buluyor ve oradaki performans incelemesinin yetersizliğine değiniyor. Söylediklerine göre Web Framework Benchmarks'taki testler data çok çatıların parsing yeteneklerini ölçüyormuş; normal işlem hızlarını değil.

Ali
Avatar
Salih Dinçer #13
Üye Ock 2012 tarihinden beri · 1908 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Merhaba,

Zafer'in makalesinden faydalanarak vibe.d'yi, hemen hali hazırdaki Windows sistemimde denemek istedim. Tabi hala istediğime (ucuz bir hosting server'da D uygulaması çalıştırmaya) uzağım! Sanırım bu tür uygulamalar için bir VPS kiralamakda fayda var, ne dersiniz?

Geçtiğim basamaklar ise şöyle:
1- Zaten DMD'yi buradan yüklemiştim ve konsol açıp "\d\dmd2vars32.bat" komutunu verdim

2- Sonra vibe.d master kopyasını D'nin yüklendiği yere indirip "cd\d" komutunu verdim

3- Tabi şurada belirtildiği gibi derleme yapabilmek için dub'ı kurmak gerekiyordu. Kurdum ve kendini Program Files'da buldum: C:\D>dir "\program files\d*."

4- Aynı anda dub komutunu da işletebilmek için şu komutu vermek gerekli: path=%path%;"C:\Program Files\dub"

5- Hali hazırda \vibe.d dizin içinde vibe.sdl bulunduğundan içine girip "dub run" komutunu vermek yeterliydi...

6- Artık 309.248 kb.'lık vibed.lib dosyası oluşturabilmiştim ve örnekleri derleyebilirdim! Örneklerin bulunduğu dizine girip aynı komutla çalıştırdım.

Örneğin ilk denediğim "Interrupt" butonuna sahip olan ve kendi kendini kapatan bir uygulama "task control" kısmen konu ile ilgili. Belki Zafer'in istediğinin tam tersi  :scared:

Sevgiler, saygılar...
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #14
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4481 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #9
Salih Dinçer on 2017-06-06, 13:00:
Ben bu çatıya hiç alışamadım. Kontrol bende değilmiş izlenimi veriyor. D'nin kendi olanaklarıyla hareket etmek isterdim.

O zaman Adam Ruppe'nin web modülleri tam sana göre. Adam ("adam" anlamında değil, özel isim Adam :) ) şu konuda örnek kod ve bağlantı veriyor:

  http://forum.dlang.org/post/kimurnsknlbrzkovuzqr@forum.dla…

Ali
acehreli (Moderatör) #15
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4481 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
vibe.d'nin yazarı Sönke Ludwig de o konuda yorum yapmış. Thread'lerle ilgili bir kaç sorunu gidermiş. vibe.d'nin bundan sonraki karşılaştırmalarda daha iyi sonuç vereceğini düşünüyor.

Ali
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Sayfa:  1  2  sonraki 
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-09-26, 07:41:28 (UTC -07:00)