6. エンコーディングとデコーディング

以下では、MIME や ACE などのデコード/エンコード処理について記載する。なお、Unicode の UTF のデコード、ACE デコード、共通 ROT 処理 (1.99 では)、iso-2022 からの Unicode 呼び出しはデコード扱いであるため、以下の説明が適用される。一方、出力側は、MIME/ACE/base64 などのみを扱い、UTF への変換はエンコードとは別に処理する。出力では ROT や Unicode 呼び出しは扱わない (サポートしていない)。

6.1 デコード
  基本的には、in_converter.c での文字取得前にデコード関数を呼び出すことで行われる。デコードと文字取り出しの境は一致しないので、内部でバッファしている。これには、prequeue と postqueue の二種類のキューが使われている。ここではこの処理を詳説する。
  
  6.1.1 文字取り出し
    基本的には、バッファからストリームで取り出すことについてはデコードしていない場合と変わらない。ただし、デコード処理では複数文字を見るため、「ここまで処理済」という着目点をもつ。この前後のバッファが prequeue および postqueue である。
    
    (文字バッファ)→ prequeue → (着目点) → postqueue → in_converter.c への取り出し
    
  6.1.2 デコードへの出入り
    デコード関数は in_converter での取り出しと非同期に動いているわけではないので、この取り出し時に呼ばれることによって適当にバッファリングしながら postqueue にデコード済の文字が入った時点でもどっている。デコードの入りは内部で検出しているが、例えば MIME では内部のエンコーディングと外のエンコーディングは一致しないため、そのままのデコード結果を返しても上で処理できない。内部で上に合わせた変換を行うことも考えられなくはないが、デコード時のエンコーディング処理結果はエンコーディングが定まっており、再度上部のエンコーディング処理を行う必要はないため、skf では上部のエンコーディングを切り替えてしまっている。具体的には、以下のような動作を行っている。

   (1) デコード動作起動検出時点で、現在のエンコーディングを退避して指定されたエンコーディングを設定後、未処理の文字列を prequeue に積んで、in_converter に sOCD を返す。    
   (2) in_converter では sOCD を受け取った時点で処理を抜けて sOCD を返して帰る。
   (3) そのまま、sOCD を持ったまま skf.c の skf_in_converter() まで戻り、ここで sOCD を検出して再び preconvert() を呼ぶ。
   (4) preconvert では、エンコーディングが設定されているためそのまま抜け、in_converter 内の所定関数を呼ぶ。
   (5) 所定関数内でデコード処理が呼ばれ、デコード処理を行う。
   (6) デコード終了を検出した場合は、そこまでの処理結果を prequeue/postqueue に積み、退避したエンコーディングを再設定し、sOCD を積んで戻る。以降 (3), (4) と進み処理継続。
    
    この処理は、処理が preconvert() 内にあるときも同様に行われることに注意。つまり、現在のエンコーディングが未検出の状態の場合、デコード対象ストリーム内のエンコーディングがセットされることはない。また、上記のエンコーディングの退避では、改行の状態も退避・回復される。
    上記にあるように、エンコーディングの退避は一段だけであり、デコード処理中はデコード開始の検出は抑止する。iso-2022 から UTF-8 を呼ぶ場合はこの処理内で行われるため、同様の動作となる。
    
  6.1.3 不正なエンコードシーケンス
    strict 指定の場合、skf では、UTF-8 の冗長なエンコード以外の不正値は原則検出していない。

6.2 MIME エンコード
  skf は nkf 同様ストリーム処理を行なっており、ある程度貯めて MIME 文字数指定になるように切り出しているわけではない。この関係で、skf では oconv() の場所では掴んだ文字が何バイトに展開されるかの正確な値は知る方法が無いので、ややトリッキーだが、以下の処理としている。

(1) mime_clip_test(): oconv() の時点で、現状で、掴んだ文字を出力すると指定した文字数 (既定では 76 文字) を超えるかどうかを判定して (この判定は比較的容易)、超えるようなら改行を入れる。また、この箇所で MIME 開始を検出してヘッダを作成する。
(2) o_c_encode(): SKFputc() をフックして、この内容をエンコードする。なにかない限りこの中では改行は通常吐かない。

  この処理のため、エンコード結果が 80 文字を超えることはまずないはず (早めに折り返すことももちろんある)。
  
  エンコードの入りと出は以下のような判断で行われる。
  
  ＊エンコーディングの入り
     (1) MIME エンコーディング対象文字外を検出 (条件は、oconv.h の mime_safe マクロ参照)
     (2) 現在の行内の位置が所定の文字数を超えた。
     
  エンコーディングの入りの際、行のはじめまたはデリミタ文字直後のあとに現れた方からエンコーディングが開始される。このためのバッファリングは o_c_encode() 内で行われる。このバッファは、改行またはデリミタ文字を検出した場合、すべて吐き出される。
  また、skf では、エンコーディングの際に用いるコードセットは o_codeset の値であり、エンコーディング対象文字列の内容は考慮しない。また、Gn を差し替えていた場合、差し替えは元コードセットのみに適用される。
  
  ＊エンコーディングの出
     (1) 改行、または EOF を検出
     (2) !mime_persistant 時、MIME 終端として規定された文字
     (3) is_white で、次の何らかの終端まで mime_safe な文字しか現れない場合。
     
   (2) は、メールヘッダでメールアドレスが並んでいる場合の検出である。(3) は、単純空白文字でそのまま抜けてしまうと、次の文字列も MIME エンコードした場合に空白情報が抜けてしまうことへの対処で、o_encode_pend をセットして入力を保留バッファに積み、確定時に吐き出している。(2)、(3)の処理は 1.99 からの対処である。
   MIME エンコード時に指定文字数を超えた場合は、折り返して直後に再度ヘッダを作っており、エンコーディング情報はそのままとしている。

6.3 正規分解
  現状基本的には、Unicode 6.1 の UnicodeData 規定のままである。Apple 互換指定時は、Apple のマップに従い、未規定の部分に関してはなにもしない。

6.4 正規合成
  skf 1.9x では、日本語カタカナ以外の正規合成は対応していない。
  
