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;
}