glibc iconv Gerçeklemesi
Önceki Soysal Karakter Kümesi Dönüşümü Sonraki
glibc iconv Gerçeklemesi
Bir önceki bölümde iconv gerçeklemelerinin sorunlarını okuduktan sonra GNU C kütüphanesindeki gerçeklemenin bu sorunlardan hiçbirine yol açmadığını söylemek elbette iyi olacaktır. Geliştirme şimdiki durumuna göre (Ocak 1999) genişletilerek sürdürülmektedir. iconv işlevlerinin geliştirmesi henüz bitmemiş olmakla birlikte temel işlevsellik değişmeyecektir.
GNU C kütüphanesinin iconv gerçeklemesi dönüşümleri gerçekleştirmek için paylaşımlı yüklenebilir modülleri kullanır. Kütüphanenin kendi içinde yerleşik olan dönüşümlerin sayısı çok azdır fakat bunlar oldukça önemsiz dönüşümlerdir.
Yüklenebilir modüllerin tüm faydalarından GNU C kütüphanesindeki gerçeklemede yararlanılmıştır. Arayüz iyi belgelendiğinden (aşağıya bakınız), bu özellikle cezbedicidir ve bundan dolayı yeni dönüşüm modüllerini yazmak kolaydır. Yüklenebilir modüllerin kullanımından kaynaklanan sakıncalar GNU C kütüphanesinde en azından ELF sistemlerde sorun çıkarmaz. Durağan olarak ilintili çalıştırılabilir nesneler olsalar bile, kütüphane, paylaşımlı nesneleri yükleyebildiğinden, durağan ilintilemenin iconv kullanılmak istendiğinde yasak olmaması gerekir.
Bahsi geçen sorunlardan ikicisi desteklenen dönüşümlerin sayısı ile ilgiliydi. Şu anda GNU C kütüphanesi yüzelliden fazla karakter kümesini desteklemektedir. Bu karakter kümeleri arasında deteklenen dönüşümlerin sayısı ise 22350'den (150 çarpı 149) fazladır. Bir karakter kümesinden diğerinde dönüşüm eksikse kolayca eklenebilir.
Bu yüksek sayıdan dolayı kısmen etkileyici olmakla birlikte GNU C kütüphanesindeki iconv gerçeklemesi önceki bölümde bahsedilen üçüncü sorundan da muaftır (A’dan B’ye ve B’den C’ye dönüşüm mümkünse A’dan C’ye doğrudan dönüşüm de daima mümkün olmalıdır da mümkün mü acaba sorunu). iconv_open işlevi bir hata ile dönerse ve errno değişkenine EINVAL değerini atarsa bu, istenen dönüşümün doğrudan ya da dolaylı olarak mümkün olmadığı anlamına gelir.
Üçgenlere bölme her karakter kümesinin diğerine dönüşümü arada UCS-4 kodlu ISO-10646 dönüşümü kullanılarak gerçekleştirilir. Ara dönüşüm için ISO 10646 karakter kümesinin kullanılmasıyla üçgenlere bölmek mümkün olur.
Yeni bir karakter kümesi için ISO 10646 kümesinde dönüşümün gerekliliğinden bahsetmek mümkün olmadığı gibi diğer karakter kümelerine dönüşümde ISO 10646'nın ne kaynak ne de hedef karakter kümesi olarak kullanılmasının gerekliliğinden bahsetmek mümkündür. Mevcut dönüşümlerin tamamı basitçe birbiriyle alakalı dönüşümlerdir.
Şu an mevcut olan tüm dönüşümler dönüşümü gereksiz yere yavaşlatan yukarıda bahsedilen üçgenlere bölme yöntemini kullanırlar. Eğer örneğin, ISO-2022-JP ve EUC-JP gibi bazı karakter kümeleri arasındaki dönüşümün hızlı olması istenirse aradan ISO 10646 çıkarılıp iki karakter kümesi arasında doğrudan bir dönüşüm yaptırılabilir. Bu iki karakter kümesi ISO 10646 ya nazaran birbirine daha benzer karakter kümeleridir.
Böyle bir durumda yeni bir dönüşümü yazmak ve daha iyi bir seçenek üretmek kolaydır. GNU C kütüphanesinin iconv gerçeklemesi eğer daha verimli olacağı belirtilirse dönüşümü gerçekleştiren modülü özdevinimli olarak kullanacaktır.
gconv-modules dosyalarının biçimi
Kullanılabilir dönüşümler hakkındaki bilgilerin tamamı gconv-modules adı verilen bir dosyanın içinde yeralır. Bu dosyanın yeri GCONV_PATH ortam değişkeninde kayıtlıdır. gconv-modules dosyaları satırlardan oluşan metin dosyalarıdır. Dosyadaki her satır şöyle yorumlanır:
  • Bir satırdaki boşluk olmayan ilk karakter bir # karakteri ise bu satırın sadece açıklamaları içerdiği varsayılır ve içeriği yorumlanmaz.
  • alias ile başlayan satırlar karakter kümesi için bir takma ad tanımlar. Bu satırlar üzerinde iki sözcük olması beklenir. İlk sözcük takma adı, ikinci sözcük ise karakter kümesinin gerçek adını belirtir. Bu satırlarda belirtilen takma adları iconv_open işlevinin kaynak_kod veya hedef_kod parametrelerinde kullanırsanız gerçek karakter kümesi ismi kullanılmış gibi işlem yapılır.
    Bir karakter kümesinin birçok farklı isim aldığı duruma sıkça rastlanır. Genelde karakter kümesinin resmi ismi yerine halk dilindeki ismi kullanılır. Bundan başka bir karakter kümesinin çeşitli nedenlerle oluşturulmuş özel isimleri de olabilir. Örneğin ISO tarafından belirtilen tüm karakter kümeleri nnn kayıt numarası olmak üzere ISO-IR-nnn biçiminde bir takma isme sahiptir. Bu, yazılımlarca karakter kümesi isimlerini oluşturan kayıt numaralarının bilinmesini ve bunların iconv_open çağrılarında kullanılabilmesini mümkün kılar. Bir karakter kümesi ile ilintili tüm takma isimler altalta ayrı satırlarda belirtilebilir.
  • module ile başlayan satırlar mevcut dönüşüm modüllerini içerir. Bu satırlar üç veya dört sözcük içermelidir.
    İlk sözcük kaynak karakter kümesini, ikincisi hedef karakter kümesini, üçüncüsü ise bu dönüşümde yüklenecek olan modülün dosya ismini belirtir. Dosya ismi paylaşımlı nesne dosyalarının uzantısını (normalde .so) içermemelidir. Bu dosyaların gconv-modules dosyasının bulunduğu dizinde olduğu varsaylır. Satırdaki son sözcük isteğe bağlıdır. Dönüşüm bedelini gösteren bir tamsayıdır ve belirtilmemişse öntanımlı değeri olan 1 olduğu varsayılır. Dönüşümü gerçekletirecek alt dönüşümlerin sayısını belirtir. Bedel değerinin kullanımını bir örnekle açıklayalım.
Yukarıdaki örneğe dönersek, ISO-2022-JP ile EUC-JP karakter kümeleri arasında doğrudan dönüşüm için bir modülün varlığından sözetmiştik. Her iki yönde de dönüşüm tek bir modül tarafından yapılır ve bu modülün dosya ismi ISO2022JP-EUCJP.so'dur. Dosyanın bulunduğu dizindeki gconv-modules dosyasının içeriğinde bu modülün tanımı şöyle olurdu:
module  ISO-2022-JP//   EUC-JP//        ISO2022JP-EUCJP    1
module  EUC-JP//        ISO-2022-JP//   ISO2022JP-EUCJP    1
Bu iki satırın neden yeterli olduğunu anlamak için iconv tarafından bunların nasıl kullanıldığını anlamak gerekir. Bu soruna yaklaşım oldukça basittir.
iconv_open işlevinin ilk çağrısında yazılım tüm gconv-modules dosyalarını okur ve iki tablo oluşturur: Biri bilinen takma adları diğeri dönüşümler ve bunları gerçekleştiren paylaşımlı nesneyi içerir.
iconv'de dönüşüm yolunun bulunması
Olası dönüşümlerin listesi önemli kenarların oluşturduğu bir çizge şeklinde tarif edilebilir. Kenarların önemini gconv-modules dosyalarında belirtilen bedeller belirler. iconv_open işlevi kaynak ve hedef karakter kümeleri arasındaki en kısa yolu bulmak için böyle bir çizgeyle çalışan bir algoritma kullanır.
iconv gerçeklemesinin neden örneğin ISO-2022-JP ve EUC-JP karakter kümeleri arasındaki dönüşümlerde kütüphane ile gelen dönüşümler yerine gconv-modules dosyalarında belirtilen dönüşüm modülünü kullandığını açıklamak artık kolaydır. Kütüphane ile gelen dönüşümler, dönüşümü iki adımda gerçekleştirir (ISO-2022-JP -> ISO 10646 ve ISO 10646 -> EUC-JP). Bu durumda bedel = 1 + 1 = 2 olur. Yukarıda ise gconv-modules dosyasında bu dönüşümün bedeli 1 olan bir dönüşüm modülü ile gerçekleştirilebileceği belirtilmiştir.
Yukarıdaki (ve ayrıca GNU C kütüphanesi ile gelen) gconv-modules dosyası ile ilgili gizemli bir öğe module satırlarında belirtilmiş karakter kümesi isimleridir. Bu isimlerin bazıları neden // ile biter? Bu şekilde biten isimler birer düzenli ifade olduklarından öyle biterler. Heceleme ile ilgilenmiyorsanız bu size bir şey ifade etmeyecektir. Kusura bakmayın! Gerçeklenimin bu kısmı henüz bitmedi. Şimdilik sadece örnekleri izlemekle yetinin, lütfen. Gerçeklenim tamamlanınca herşey daha iyi olacak. -drepper
gconv-modules hakkındaki son düşünceler // ile bitmeyen isimlerle ilgilidir. INTERNAL diye bir karakter kümesi ismi sık sık geçer. Bu karakter kümesi ismi yukarıdaki dönüşüm tartışmasında üçlemenin ara adımını oluşturan karakter kümesini nitelemek için seçilmiştir. Bunun aslında UCS-4 olduğundan bahsetmişsek de bu tamamen doğru değildir. UCS-4 belirtimi, kendi içinde bayt sıralaması için kullanılan bir belirtim daha içerir. Bir UCS-4 değeri 4 bayttan oluştuğundan saklanan değer bayt sıralamasından etkilenir. Eğer işlemcinin (en azından çalışan sürecin) bayt sıralaması UCS-4 için gerekenle aynı değilse, değerin dahili gösterimi UCS-4’teki ile aynı olmayacaktır. Dönüşüm sırasında ara dönüşüm ürününün durumu ile ilgilenmeyen biri için bayt takaslama işlemlerine zaman harcamanın gereği yoktur. Bayt sıralama sorunlarından kaçınmak için, en kıymetli baytın en düşük adrese yerleştirildiği bayt sıralamasını kullanan sistemlerle aynı bayt sıralamasına sahip dahili karakter kümesine INTERNAL ismi verilmiştir.
iconv modülü veri yapıları
Bu bölüme kadar modüllerin yerlerinin kullanımı açıklandı. Burada yeni bir modül yazmak için kullanılacak arayüz açıklanacaktır. Bu bölümde açıklanacak arayüz Ocak 1999'dan beri kullanılmaktadır. Arayüz gelecekte biraz değişecekse de bu değişiklik uyumluluk korunarak yapılacaktır.
Yeni bir modülü yazmak için gerekli tanımlar standartdışı bir başlık dosyası olan gconv.h içindedir. Aşağıda, bu dosyada bulunan tanımlar şimdilik sadece bir önbilgi verecek kadar açıklanmıştır.
iconv kullanıcısının bakış açısından arayüz basittir: iconv_open işlevi iconv çağrılarında kullanılabilen bir tanıtıcı ile döner. Bu tanıtıcının görevi sona erdiğinde bir iconv_close çağrısı ile tanıtıcı serbest bırakılır. Burada sorun, tanıtıcı iconv işlevine aktarılan herşey olduğundan tanıtıcının tüm dönüşüm adımlarını ve ayrıca her dönüşümün durum bilgisini tutması zorunluluğudur. Bu nedenle, gerçeklemeyi anlamak için en önemi elemanlar aslında bu veri yapılarıdır.
İki farklı veri yapısına ihtiyacımız var. İlki dönüşümü, ikincisi da durumu, v.s. yi açıklamak için. Aslında gconv.h dosyasında bunun gibi iki tanım vardır.
struct __gconv_step
veri türü
Bu veri yapısı bir modülün gerçekleştirdiği bir dönüşümü açıklar. Dönüşüm işlevleri ile yüklenen bir modüldeki her işlev için bu türde tek bir nesne vardır (bu nesne asıl dönüşüme ilişkin bir bilgi içermez, sadece dönüşümün kendisini tanımlar).
struct __gconv_loaded_object *__shlib_handle
const char *__modname
int __counter
Yapının bu elemanları C kütüphanesi tarafından dahili olarak kullanılır ve paylaşımın yüklenmesini ve kaldırılmasını yönetirler. Birinin kullanılmış olması diğerlerinin kullanılmasını ya da ilklendirilmesini gerektirmez.
const char *__from_name
const char *__to_name
__from_name ve __to_name alanları kaynak ve hedef karakter kümelerinin isimlerini içerir. Bir modül birden fazla karakter kümesi ve yönde dönüşüm için kullanılabildiğinden asıl dönüşümü tanımlamakta kullanılır.
gconv_fct __fct
gconv_init_fct __init_fct
gconv_end_fct __end_fct
Bu alanlar yüklenebilir modüldeki işlevlere göstericileri içerir. Arayüz aşağıda açıklanacaktır.
int __min_needed_from
int __max_needed_from
int __min_needed_to
int __max_needed_to;
Bu değerler modülün ilklendirme işleviyle atanmalıdır. __min_needed_from değeri kaynak karakter kümesinin en az kaç bayt gerektirdiğini belirtir. __max_needed_from değeri azami değer ile ayrıca olası öteleme dizilimlerini belirtir.
The __min_needed_to ve __max_needed_to ise benzer değerleri hedef karakter kümesi için içerir.
Bu değerlerin doğruluğu son derece önemlidir, çünkü aksi takdirde dönüşüm işlevleri sorun çıkaracak ve bekleneni yapmayacaktır.
int __stateful
Bu eleman da ilklendirme işlevi ile ilklendirilmelidir. Kaynak karakter kümesi durumsal ise bu elemanın değeri sıfırdan farklı olacaktır.
void *__data
Bu eleman modüldeki dönüşüm işlevleri tarafından serbestçe kullanılabilir. void *__data bir çağrıdan diğerine fazladan bilgi aktarmak için kullanılabilir. Gerekmedikçe ilklendirilmesi gerekmez. Elemana özdevimli ayrılmış bir bellek alanının göstericisi atanmışsa (büyük ihtimalle ilklendirme işlevi tarafından), kullanılacak son işlevin bu alanı serbest bırakması sağlanmalıdır. Aksi takdirde uygulama bellek kaçağına sebep olur.
Bu veri yapısının bu belirtim dönüşümünün tüm kullanıcıları tarafından paylaşılmasının sağlanması önemlidir. Bu nedenle, __data elemanının, dönüşüm işlevinin belli bir kullanımına özgü veri içermemesi gerekir.
struct __gconv_step_data
veri türü
Bu veri türü dönüşüm işlevlerinin kullanımına özel bilgileri içerir.
char *__outbuf
char *__outbufend
Bu elemanlar dönüşüm adımında kullanılan çıktı tamponuna ilişkin veriyi içerir. __outbuf elemanı tamponun başlangıcını, __outbufend elemanı ise tamponun son baytını belirtir. Dönüşüm işlevi tamponun boyutunun herşeye yeterli olduğunu varsaymamalı, ancak en azından tam bir karakter için tamponda yeterli yer olduğunu varsaymalıdır.
Dönüşüm bittikten sonra, eğer dönüşüm son adımdaysa, son bayt tampona yazıldıktan sonra ne kadar mevcut çıktı olduğunu belirtmek için __outbuf elemanı değiştirilmelidir, __outbufend elemanı değiştirilmemelidir.
int __is_last
Dönüşüm işlemi son adımdaysa bu elemanın değeri sıfırdan farklıdır. Bu bilgi yineleme için gereklidir. Aşağıdaki dönüşüm işlevlerinin iç yapıları ile ilgili açıklamalara bakınız. Bu eleman asla değiştirilmemelidir.
int __invocation_counter
Dönüşüm işlevi bu elemanı kaç defa çağrıldığı bilgisini tutmak için kullanabilir. Bazı karakter kümeleri dönüşüm işlevinin ilk adımında bazı çıktılar üretirler. Bu alan bu ilk adımın belirlenmesi için kullanılabilir. Bu eleman asla değiştirilmemelidir.
int __internal_use
Bu eleman belli bazı durumlar için kullanılabilen alanlardan biridir. Dönüşüm işlevleri mbsrtowcs işlevini gerçeklemekte kullanılmışsa (yani, işlev iconv gerçeklemesi üzerinden doğrudan kullanılmamışsa) bu alana sıfırdan farklı bir değer atanır.
mbsrtowcs işlevlerinin normalde bütün metni dönüştürmek için tek tek dizgeleri dönüştürmek üzere defalarca çağrılmasına karşın iconv işlevlerinin metnin tamamını dönüştürmekte kullanılması gibi bir fark oluşur.
Fakat bu durumda karakter kümesinin belirtimindeki bazı kuralların yerine getirilmesiyle ilgili bazı sorunlarla karşılaşılır. Bazı karakter kümeleri metnin tamamı için bir defalığına ilk adımda bir çıktı verilmesini gerektirir. Eğer metni dönüştürme işlemi mbsrtowcs işlevinin defalarca çağrılmasını gerektiriyorsa, ilk çağrıda bu çıktı verilmelidir. Bununla birlikte, mbsrtowcs işlevinin çağrıları arasında iletişim olmadığından dönüşüm işlevlerinin bu çıktıyı vermesi mümkün olmaz. Bu durum tanıtıcı sayesinde bu bilgiye erişebilen iconv çağrılarından farklı bir durumdur.
int __internal_use elemanı çoğunlukla __invocation_counter elemanı ile birlikte ağağıdaki gibi kullanılır:
if (!data->__internal_use
      && data->__invocation_counter == 0)
  /* İlk adım çıktısını bas.  */
  …
Bu elemanın değeri asla değiştirilmemelidir.
mbstate_t *__statep
__statep elemanı mbstate_t (bkz. Durumun saklanması) türünde bir nesneye göstericidir. Durumsal bir karakter kümesi dönüşüm durumu hakkındaki bilgileri saklamak için __statep ile gösterilen alanı kullanmalıdır. __statep elemanı kendini asla değiştirmemelidir.
mbstate_t __state
Bu eleman asla doğrudan değiştirilmemelidir. Yapının kullandığı alanın belirtildiği bir elemandır.
iconv modül arayüzleri
Veri yapıları hakkında bilgi edindikten sonra dönüşüm işlevlerinin açıklamalarına girebiliriz. Arayüzü anlayabilmek için dönüşüm nesnelerini yükleyen C kütüphanesindeki işlevsellik hakkında biraz önbilgi vermek gerekir.
Bir dönüşümün defalarca kullanıldığı duruma sıklıkla rastlanır (yazılımın çalışması esnasında aynı karakter kümesi için çok sayıda iconv_open çağrısı yapılması). GNU C kütüphanesindeki mbsrtowcs ve benzeri işlevler de aynı işlevin kullanım sayısını arttıran iconv işlevselliğini kullanırlar.
Dönüşümlerin böyle defalarca kullanılabilmesinden dolayı modüller her dönüşüm için tekrar yüklenmezler. Modül bir kere yüklendikten sonra aynı iconv veya mbsrtowcs çağrıları defalarca yapılabilir. Bilgilerin işleve özel dönüşüm bilgileri ve dönüşüm verisi bilgileri olarak ayrılabilmesi sayesinde bu mümkün olur. Bunun yapılmasını sağlayan iki veri yapısından bir önceki bölümde bahsedilmişti.
Bu ayrıca, modül tarafından sağlanan işlevlerin sözdiziminde ve arayüzün kendisinde de yansıtılır. Aşağıdaki isimlere sahip olması zorunlu üç işlev vardır:
gconv_init
gconv_init işlevi, dönüşüm işlevine özel veri yapısını ilklendirir. Bu nesne bu dönüşümü kullanan tüm dönüşümlerce paylaşılır ve bu nedenle, dönüşümün kendisi ile ilgili durum bilgisi burada saklanmaz. Bir modül birden fazla dönüşümü gerçekleştirebiliyorsa gconv_init işlevi defalarca çağrılabilir.
gconv_end
gconv_end işlevi, gconv_init işlevi ile ayrılan tüm özkaynakları serbest bırakmak için kullanılır. Böle bir işlem gerekmiyorsa bu işlev tanımlanmayabilir. Modül birden fazla dönüşümü gerçekleştirebiliyorsa ve gconv_init işlevi her dönüşüm için aynı özkaynakları ayırmıyorsa bu işlevi tanımlarken dikkatli olunmalıdır.
gconv
Asıl dönüşüm işlevidir. Bir metin bloğunu dönüştürmekte kullanılır. Dönüşüm işlevlerinin bu amaca yönelik dönüşüm verisi ve gconv_init ile ilklendirilen dönüşüm adım bilgisi işleve aktarılmalıdır.
Bu üç modül arayüz işlevi için tanımlanmış üç veri türü vardır ve bunlar arayüzü tanımlar:
int (*__gconv_init_fct)
(struct __gconv_step *)
işlev
Modülün gerçekleştirdiği her dönüşüm için sadece bir kere çağrılan ilklendirme işlevinin arayüzünü belirler.
struct __gconv_step veri yapısının açıklamasında değinildiği gibi ilklendirme işlevi bu veri yapısının elemanlarını ilklendirir.
__min_needed_from
__max_needed_from
__min_needed_to
__max_needed_to
Bu elemanlar kaynak ve hedef karakter kümelerindeki bir karakteri oluşturan bayt sayısının azami ve asgari değerleri ile ilklendirilmelidir. Karakaterlerin hepsi aynı bayt sayısı ile ifade ediliyorsa azami ve asgari değerler aynı olacaktır.
__stateful
Bu eleman kaynak karaker kümesinin durumsal olması halinde sıfırdan farklı, aksi takdirde sıfır olmalıdır.
İlklendirme işlevinin dönüşüm işlevi ile bilgi alışverişi yapması gerekliyse, bu iletişim __gconv_step yapısının __data elemanı kullanılarak yapılabilir. Ancak, bu veri tüm dönüşümlerce paylaşılacağından dönüşüm işlevleri bu veriyi değiştirmemelidir. Bunun yapılışına bir örnek:
#define MIN_NEEDED_FROM         1
#define MAX_NEEDED_FROM         4
#define MIN_NEEDED_TO           4
#define MAX_NEEDED_TO           4

int
gconv_init (struct __gconv_step *step)
{
  /* Dönüşüm yönünü belirleyelim.  */
  struct iso2022jp_data *new_data;
  enum direction dir = illegal_dir;
  enum variant var = illegal_var;
  int result;

  if (__strcasecmp (step->__from_name, "ISO-2022-JP//") == 0)
    {
      dir = from_iso2022jp;
      var = iso2022jp;
    }
  else if (__strcasecmp (step->__to_name, "ISO-2022-JP//") == 0)
    {
      dir = to_iso2022jp;
      var = iso2022jp;
    }
  else if (__strcasecmp (step->__from_name, "ISO-2022-JP-2//") == 0)
    {
      dir = from_iso2022jp;
      var = iso2022jp2;
    }
  else if (__strcasecmp (step->__to_name, "ISO-2022-JP-2//") == 0)
    {
      dir = to_iso2022jp;
      var = iso2022jp2;
    }

  result = __GCONV_NOCONV;
  if (dir != illegal_dir)
    {
      new_data = (struct iso2022jp_data *)
        malloc (sizeof (struct iso2022jp_data));

      result = __GCONV_NOMEM;
      if (new_data != NULL)
        {
          new_data->dir = dir;
          new_data->var = var;
          step->__data = new_data;

          if (dir == from_iso2022jp)
            {
              step->__min_needed_from = MIN_NEEDED_FROM;
              step->__max_needed_from = MAX_NEEDED_FROM;
              step->__min_needed_to = MIN_NEEDED_TO;
              step->__max_needed_to = MAX_NEEDED_TO;
            }
          else
            {
              step->__min_needed_from = MIN_NEEDED_TO;
              step->__max_needed_from = MAX_NEEDED_TO;
              step->__min_needed_to = MIN_NEEDED_FROM;
              step->__max_needed_to = MAX_NEEDED_FROM + 2;
            }

          /* Evet, bu durumsal bir kodlama.  */
          step->__stateful = 1;

          result = __GCONV_OK;
        }
    }

  return result;
}
İşlev önce hangi dönüşümün istendiğine bakar. Bu işlev ile ele alınan modül dört farklı dönüşümü gerçekleştirmektedir; hangisinin kullanılmak istendiği isimlere bakarak saptanabilir. Karşılaştırma daima harf büyüklüğünden bağımsız olarak yapılmalıdır.
Sonra, seçilen dönüşüm için gerekli bilgileri içeren veri yapısına sıra geliyor ve buna yer ayrılıyor. struct iso2022jp_data yerel olarak modülün dışında tanımlandığından bu veri işlev dışında kullanılamaz. Eğer modülün desteklediği dört dönüşümün tamamı için dönüşüm istenseydi, dört veri bloğu olacaktı.
İlginç olan veri nesnesinin __min_ ve __max_ elemanlarının ilklendirme adımıdır. Tek bir ISO-2022-JP karakteri bir bayttan dört bayta kadar uzunlukta olabilir. Bundan dolayı burada MIN_NEEDED_FROM ve MAX_NEEDED_FROM makroları kullanılmıştır. Çıktı daima dahili karakter kümesi (UCS-4) olacağından her karakter daima dört bayt uzunlukta olacaktır. Dahili karakter kümesinden ISO-2022-JP karakter kümesine dönüşüm için önceleme dizilimlerinin karakter kümeleri arasında geçiş yapmak için gerekli olduğunu hesaba katmak zorundayız. Bu nedenle, bu yön için __max_needed_to elemanına MAX_NEEDED_FROM + 2 değeri atanmaktadır. Böylece diğer karakter kümesine geçiş için gekeli olan önceleme dizilimleri hesaba katılmış olur. İki yöndeki azami değerler arasındaki dengesizlik kolayca açıklanabilir: ISO-2022-JP metin okunurken önceleme dizilimleri tek başlarına elde edilebilir (yani, önceleme diziliminin etkisi durum bilgisi içinde kaydedilmiş olacağından önceleme dizilimi bir gerçek karakterin işlenmesinde gerekli değildir). Diğer yönde durum farklıdır. Hangi karakter kümesinin sonra geleceği genelde bilinmediğinden durumu değiştirecek önceleme dizilimleri ileriye dönük hesaba katılamaz. Bu, önceleme dizilimlerinin sonraki karakter ile birlikte ele alınması zorunluluğu demektir. Bu nedenle karakterin gerektirdiğinden daha fazla alan gereklidir.
İlklendirme işlevinin olası dönüş değerleri şunlardır:
__GCONV_OK
İlklendirme başarılı.
__GCONV_NOCONV
İstenen dönüşümü bu modül desteklemiyor. Bu durum, gconv-modules dosyası hatalıysa oluşabilir.
__GCONV_NOMEM
Ek bilginin saklanacağı bellek ayrılamadı.
Modül yüklenmeden işlevin çağrılması önemce daha kolaydır. Çoğunlukla hiçbir şeye sebep olmaz; tamamen ihmal edilebilir.
void (*__gconv_end_fct)
(struct gconv_step *)
işlav
Bu işlevin görevi ilklendirme işlevinin ayırdığı tüm özkaynakları serbest bırakmaktır. Bu nedenle argüman olarak nesnenin sadece __data elemanını kullanır. İlklendirme işlevi ile ilgili örneğe devam edersek dönüşümü sonlandıran işlev şöyle olurdu:
void
gconv_end (struct __gconv_step *data)
{
  free (data->__data);
}
En önemli işlev, karmaşık karakter kümeleri için oldukça karışık olabilen dönüşüm işlevinin kendisidir. Daha fazlası gerekli olmadığından burada işlevin sadece iskeletinden bahsedilecektir.
int (*__gconv_fct)
(struct __gconv_step *,
 struct __gconv_step_data *,
 const char **,
 const char *,
 size_t *,
 int)
işlev
Dönüşüm işlevinin çağrılmasını gerektiren iki sebep olabilir: Metni dönüştürmek ya da durumu sıfırlamak. iconv işlevinin açıklamasına bakılırsa boşaltma kipinin neden gerekli olduğu görülebilir. Hangi kipin seçilmiş olduğu bir tamsayı olan altıncı argümana bakılarak saptanır. Boşaltma kipi seçilmişse bu argümanın değeri sıfırdan farklı olacaktır.
Çıktı tamponunun yerinin her iki kip için ortak olduğu görülür. Bu tampon hakkındaki bilgi dönüşüm adım verisinde saklanır. Bu bilgiye ilişkin gösterici işleve ikinci argüman olarak aktarılır. struct __gconv_step_data yapısının açıklaması dönüşüm adım verisi hakkında daha fazla bilgi içerir.
Boşaltma için ne yapılacağı kaynak karakter kümesine bağlıdır. Eğer kaynak karakter kümesi durumsal değilse birşey yapmak gerekmez. Aksi takdirde, işlev durum nesnesini ilk duruma getirecek bir bayt dizilimini göndermek zorundadır. Bu yapıldıktan sonra dönüşüm zincirindeki diğer dönüşüm işlevlerine de bu imkan tanınmalıdır. Bu işlevi başka bir adımın izleyip izlemeyeceği ilk argümana adım verisinin __is_last elemanındaki bilgi aktarılarak saptanabilir.
Metnin dönüştürüldüğü kip daha ilginçtir. Bu kipteki ilk adımda girdi tamponundaki metnin olabildiğince büyük bir kısmı dönüştürülür ve sonuç çıktı tamponunda saklanır. Girdi tamponunun başlangıcı, tamponun başlangıcını gösteren bir göstericiye gösterici olan üçüncü argümandan saptanır. Dördüncü argüman tampondaki son bayttan sonraki bayta bir göstericidir.
Dönüşüm, eğer karakter kümesi durumsal ise mevcut duruma bağlı olarak uygulanır. Durum bilgisi adım verisinin __statep elemanı tarafından gösterilen bir nesnede saklanır (ikinci argüman). Girdi tamponu boşsa ya da çıktı tamponu dolmuşsa dönüşüm durdurulur. Bu durumda üçüncü parametredeki gösterici değişkeni son işlenen bayttan sonraki baytı göstermelidir (eğer tüm girdi tüketilmişse, bu gösterici ve dördüncü parametre aynı değerde olur).
Sonra ne yapılacağı bu adımın son adım olup olmamasına bağlıdır. Eğer bu adım son adımsa yapılacak tek şey, adım verisi yapısının __outbuf elemanının son yazılan bayttan sonraki baytı gösterecek şekilde güncellenmesidir. Ek olarak, beşinci parametre tarafından gösterilen size_t türündeki değişken geri dönüşümsüz olarak dönüştürülen karakter sayısı (bayt sayısı değil) kadar arttırılmalıdır. Bundan sonra işlev dönebilir.
Eğer bu adım son adım değilse, sonraki dönüşüm işlevlerine kendi görevlerini yerine getirebilmeleri imkanı sağlanmalıdır. Bu nedenle uygun dönüşüm işlevi çağrısı yapılmalıdır. İşlevler hakkındaki bilgiler dönüşüm veri yapılarında saklanır ve ilk parametre olarak aktarılır. Bu bilgi ve adım verisi dizilerde saklanır, bu durumda sonraki eleman her iki halde de basit olarak gösterici aritmetiği ile bulunabilir:
int
gconv (struct __gconv_step *step, struct __gconv_step_data *data,
       const char **inbuf, const char *inbufend, size_t *written,
       int do_flush)
{
  struct __gconv_step *next_step = step + 1;
  struct __gconv_step_data *next_data = data + 1;
  …
next_step göstericisi sonraki adım bilgisini içerirken, next_data sonraki veri kaydını içerir. Sonraki işlev çağrısı bu nedenle şöyle görünecektir:
  next_step->__fct (next_step, next_data, outerr, outbuf,
                    written, 0)
Fakat henüz bu yeterli değildir. İşlev çağrısı döndükten sonra dönüşüm işlevi biraz daha işlem yapmak zorunda kalabilir. İşlevin dönüş değeri __GCONV_EMPTY_INPUT ise, çıktı tamponunda hala yer var demektir. Girdi tamponu boş olmadıkça dönüşüm, işlevi girdi tamponunun kalanını işlemek üzere tekrar çağırır. Eğer dönüş değeri __GCONV_EMPTY_INPUT değilse bazı şeyler yanlış gitmiştir ve bunun kurtarılması gerekir.
Dönüşüm işlevi için bir gereklilik de, girdi tamponu göstericisinin (üçüncü argüman) daima çıktı tamponuna konulan dönüştürülmüş son karakteri göstermesidir. Dönüşümün uygulandığı adımda eğer daha alt adımları gerçekleştiren dönüşüm işlevleri hata verip durmazsa, çıktı tamponundaki karakterlerin tümü tüketilmemişse ve bu nedenle girdi tamponu göstericileri doğru konumu gösterecek duruma getirilmemişse bu zaten gerçekleşir.
Girdi tamponunun düzeltilmesi, eğer girdi ve çıktı karakter kümelerindeki tüm karakterler sabit genişlikteyse kolaydır. Bu durumda, çıktı tamponunda kaç karakter kaldığını hesaplayabilir ve bu sonuçtan hareketle girdi tampon göstericisini benzer bir hesaplamayla elde edebiliriz. Zor olan, karakter kümelerinin değişken genişlikte karakterler içermesi ve dönüşüm bir de durumsal ise işlemin daha da karmaşıklaşmasıdır. Bu durumlarda dönüşüm, ilk dönüşümden önceki bilinen durumdan bir daha başlatılır (gerekliyse, dönüşüm durumu sıfırlanmalı ve dönüşüm döngüsü tekrar çalıştırılmalıdır). Burada farklı olan ne kadar girdi oluşturulması gerektiğinin bilinmesi ve dönüşümün ilk işe yaramaz karakterden önce durdurulabilmesidir. Bu yapıldıktan sonra girdi tamponu göstericileri tekrar güncellenmelidir. Bundan sonra işlev dönebilir.
Üstünde durulması gereken son bir şey daha var. Dönüşümün ilk çağrısında bir iletinin çıktılanması gerektiği durumlar için çağrının ilk çağrı olup olmadığının bilinmesi için dönüşüm işlevi adım verisi yapısının __invocation_counter elmanının değerini çağrıcıya dönmeden önce arttırmalıdır. Bunun nasıl kullanıldığı hakkında daha fazla bilgi edinmek isterseniz struct __gconv_step_data yapısının açıklamasına bakınız.
Dönüş değeri şunlardan biri olmalıdır:
__GCONV_EMPTY_INPUT
Tüm girdi tüketildi ve çıktı taponunda yer kaldı.
__GCONV_FULL_OUTPUT
Çıktı tamponunda yer kalmadı. Bu değer son adımda alınmamışsa zincirdeki sonraki işlev çağrısında bu değere uygun işlem yapılmalıdır.
__GCONV_INCOMPLETE_INPUT
Bozuk bir karakter dizilimi içerdiğinden girdi tamponu tüketilememiştir.
Aşağıda bir dönüşüm işlevinin çerçevesi çizilmeye çalışılmıştır. Yeni bir dönüşüm işlevi yazılacaksa, burada boş bırakılmış yerler doldurulmalıdır.
int
gconv (struct __gconv_step *step, struct __gconv_step_data *data,
      const char **inbuf, const char *inbufend, size_t *written,
      int do_flush)
{
  struct __gconv_step *next_step = step + 1;
  struct __gconv_step_data *next_data = data + 1;
  gconv_fct fct = next_step->__fct;
  int status;

  /* İşlev girdisiz çağrılmışsa bu ilk duruma getirme anlamındadır.
     Girdinin bir kısmı işlendikten sonra bu yapılmışsa girdi atlanır. */
  if (do_flush)
    {
      status = __GCONV_OK;

      /* Durum nesnesini ilk duruma getiren bayt dizilimi gerekebilir. */

      /* Call the steps down the chain if there are any but only
        if we successfully emitted the escape sequence.  */
      if (status == __GCONV_OK && ! data->__is_last)
        status = fct (next_step, next_data, NULL, NULL,
                      written, 1);
    }
  else
    {
      /* Gösterici değişkenlerinin ilk değerlerini saklayalım.  */
      const char *inptr = *inbuf;
      char *outbuf = data->__outbuf;
      char *outend = data->__outbufend;
      char *outptr;

      do
        {
          /* Bu adımın başlangıç değerini hatırlayalım.  */
          inptr = *inbuf;
          /* The outbuf buffer is empty.  */
          outptr = outbuf;

          /* Durumsal kodlamalar için durum burada güvenceye alınmalı.  */

          /* Dönüşüm döngüsü çalıştır ve durumu uygun değere ayarla.  */

          /* Bu son adımsa, döngüden çık. Yapacak birşey kalmamış.  */
          if (data->__is_last)
            {
              /* Kullanılabilir kaç bayt kaldı bilgisini sakla.  */
              data->__outbuf = outbuf;

            /* geri dönüşümsüz dönüşüm yapılmışsa numarasını
               *written'a ekle.  */

            break;
          }

          /* Üretilen tüm çıktıyı yaz.  */
          if (outbuf > outptr)
            {
              const char *outerr = data->__outbuf;
              int result;

              result = fct (next_step, next_data, &outerr,
                            outbuf, written, 0);

              if (result != __GCONV_EMPTY_INPUT)
                {
                  if (outerr != outbuf)
                    {
                      /* Girdi tampon göstericisini sıfırla.
                         Burada karmaşık durumu belgeleyelim.  */
                      size_t nstatus;

                      /* Göstericileri yeniden yükle.  */
                      *inbuf = inptr;
                      outbuf = outptr;

                      /* Durum sıfırlanacaksa sıfırla.  */

                      /* Dönüşümü tekrar yap, ama bu sefer çıktı
                         tamponunun sonu outerr de.  */
                    }

                  /* urumu değiştir.  */
                  status = result;
                }
              else
                /* Çıktı tamamlandı, herşey tamamsa
                   sonraki adıma geçelim.  */
                if (status == __GCONV_FULL_OUTPUT)
                  status = __GCONV_OK;
          }
        }
      while (status == __GCONV_OK);

      /* Bu adımdaki işimiz bitti.  */
      ++data->__invocation_counter;
    }

  return status;
}
Yeni modül yazmak için bu kadar bilgi yeterlidir. Bunu yapmak isteyenler GNU C kütüphanesinin kaynak koduna da bakabilirler. Pek çok çalışan ve eniyilenmiş örnek bulunmaktadır.
Önceki Üst Ana Başlık Sonraki
Diğer iconv Gerçeklemeleri Başlangıç VII. Oylum - Yereller ve Uluslararasılaştırma
Bir Linux Kitaplığı Sayfası