# アプリケーションの作成方法

idnkitライブラリ (およびidnkitliteライブラリ) を用いたアプリケーション の作成方法や注意点をまとめました。

- プログラムの書き方
  - ヘッダファイル
  - ロケール設定
  - エラー表示
  - コンパイル
  - リンク
  - 共有ライブラリのリンク
- プログラム例

## プログラムの書き方

idnkitライブラリ (およびidnkitliteライブラリ) を使用したプログラムの 作成方法、および注意点などについてまとめておきます。

### ヘッダファイル

高レベルAPIを使用するプログラムでは、`idn/api.h`を インクルードしてください。 (エラーコードを定義している`idn/result.h`は、 `idn/api.h`から暗黙的にインクルードされるため、明示的に インクルードする必要はありません。)

> `#include <idn/api.h>`

低レベルAPIを使用するプログラムでは、`idn/res.h`を インクルードしてください。 (変換コンテキストを操作するresconfモジュールのヘッダファイル `idn/resconf.h`やエラーコードを定義している `idn/result.h`は、`idn/res.h`から暗黙的に インクルードされるため、明示的にインクルードする必要はありません。)

> `#include <idn/res.h>`

バージョン番号の取得関数を提供しているversionモジュールを使う場合は、 別途`idn/version.h`をインクルードして下さい。

> `#include <idn/version.h>`

### ロケール設定

idnkitライブラリ (およびidnkitliteライブラリ) は、実行時に、 ローカルエンコーディングや「現在の言語」を決定する際、ロケールの 設定情報を参照します。 そのため、アプリケーションは冒頭部分で`setlocale()`を 呼び出し、ロケールの初期化を行って下さい。 典型的な呼び出し方は、次のようになります。

> `setlocale(LC_ALL, "");`

システムやCコンパイラによっては、`setlocale()`を使用する ためには

> `#include <locale.h>`

という記述も別途必要かも知れません。 詳しくは、お使いのシステムないしCコンパイラのマニュアルを参照して下さい。

### エラー表示

idnkitライブラリ (およびidnkitliteライブラリ) のほとんどのAPIは、 `idn_result_t` 型のエラーコードを返します。 エラーコードに応じたメッセージ文字列を返す関数 `idn_result_tostring()`が用意されています。 エラーメッセージを表示する際には、この関数を使うと便利です。 ただし、エラーメッセージはロケール設定によらず英語になります。

>     idn_result_t r;

>     /* idn_result_tostring() の使用例 */
>     r = idn_encodename(IDN_ENCODE_LOOKUP, from, to, tolen);
>     if (r != idn_success)
>         printf("error: %s\n", idn_result_tostring(r));

この関数についての詳細は、 「idn_result_tostring()」 をご覧ください。

### コンパイル

アプリケーションをコンパイルする際には、idnkitのヘッダファイルの 配置場所をCコンパイラに伝える必要があるかも知れません。 もし、Cコンパイラがヘッダファイルを見つけられなかった場合は、

> `error: idn/api.h: No such file or directory`

といったエラーメッセージが出力されます。 (メッセージは、お使いのCコンパイラ等によって異なります。) GCCなど、UNIX系OSで動作する典型的なCコンパイラでは、 `-I`オプションを使ってヘッダファイルの検索パスを指定 することができます。 たとえば、`/usr/local/include/idn`の下にヘッダファイル がインストールされている場合は (最後の`/idn`を取って)、

> `gcc -I/usr/local/include -c sample.c`

のようにします。

### リンク

GCCに代表される、UNIX系OSで動作する典型的なCコンパイラを用いて アプリケーションにidnkitライブラリをリンクする際は、`-lidnkit` というオプションを付与します。

> `gcc -o sample sample.o -lidnkit`

ただし、GNU libiconvなどの外部ライブラリの提供する`iconv()` を利用した場合は、合わせてそのライブラリもリンクする必要があります。 たとえば、GNU libiconvであれば、次のようにします。

> `gcc -o sample sample.o -lidnkit -liconv`

idnkitliteライブラリをリンクする際は、`-lidnkit`の代わりに `-lidnkitlite`オプションを付与します。 idnkitliteライブラリは`iconv()`を使用しませんので、 `iconv()`を提供するライブラリのリンクは必要ありません。

> `gcc -o sample sample.o -lidnkitlite`

ただし、idnkitライブラリ、idnkitliteライブラリ、`iconv()` を提供するライブラリがすべて、Cコンパイラ (正確にはリンカ) が探し出せる 位置になくてはなりません。 見つからない場合は、

> `sample.c:(.text+0x19): undefined reference to `idn_nameinit'`

といったエラーメッセージが出力されます。 その場合、 `-L`オプションを使ってライブラリファイルの検索パスを 指定することができます。 たとえば、`/usr/local/lib`の下にライブラリが インストールされている場合は、

> `gcc -o sample sample.o -L/usr/local/lib -lidnkit`

のようにします。 ただし、共有ライブラリをリンクする際は、さらに幾つかの注意点があります。 詳しくは次項に記します。

### 共有ライブラリのリンク

共有ライブラリをリンクする際、システムによってはコンパイラやリンカに オプションを付与する必要があります。 ただ、必要となるオプションは、システムによってまちまちです。 調べてもよく分からなければ、GNU libtoolを使うのも一つの手です。 (ただし、GNU libtoolが対応しているシステムに限ります。) GNU libtoolは以下のページから入手できます。

> http://www.gnu.org/software/libtool/

GNU libtoolを使用する場合、コンパイル時は次のように "`libtool --mode=compile`" に続けてCコンパイラのコマンド行を 記します。

> `libtool --mode=compile gcc -I/usr/local/include -c sample.c`

同様に、リンク時は "`libtool --mode=link`" に続けてCコンパイラのコマンド行を 記します。

> `libtool --mode=link gcc -o sample sample.o -L/usr/local/lib -lidnkit`

できあがったコマンドを所定のディレクトリにインストールする際は、 "`libtool --mode=install`" に続けてinstallコマンドの 実行行を記します。

> `libtool --mode=install install -m755 sample /usr/local/bin`

## プログラム例

ここでは、高レベルAPIを使用して国際化ドメイン名の名前解決を行うための、 簡単なプログラムを紹介します。

>     #include <stddef.h>
>     #include <sys/types.h>
>     #include <stdio.h>
>     #include <locale.h>
>     #include <netdb.h>
>     #include <sys/socket.h>
>     #include <netinet/in.h>
>     #include <arpa/inet.h>
>     #include <idn/api.h>

>     int
>     main(int ac, char **av) {
>       struct hostent *hp;
>       char buf1[256];
>       char buf2[256];
>       char addr[100];
>       idn_result_t r;

>       /* ロケールを設定する */
>       setlocale(LC_ALL, "");
>       if (ac != 2) {
>         fprintf(stderr, "Usage: %s hostname\n", av[0]);
>         return 1;
>       }
>       /* apiモジュールを初期化する */
>       if ((r = idn_nameinit(1)) != idn_success) {
>         fprintf(stderr, "idn_nameinit: %s\n", idn_result_tostring(r));
>         return 1;
>       }
>       /* gethostbynameを呼ぶ前に名前を変換する */
>       if ((r = idn_encodename(IDN_ENCODE_LOOKUP, av[1],
>           buf1, sizeof(buf1))) != idn_success) {
>         fprintf(stderr, "idn_encodename: %s\n", idn_result_tostring(r));
>         return 1;
>       }
>       /* 名前解決を行う */
>       if ((hp = gethostbyname(buf1)) == NULL) {
>         fprintf(stderr, "gethostbyname failed\n");
>         return 1;
>       }
>       /* 返された名前をローカルエンコーディングに変換する */
>       if ((r = idn_decodename(IDN_DECODE_LOOKUP, hp->h_name,
>           buf2, sizeof(buf2))) != idn_success) {
>           fprintf(stderr, "idn_decodename: %s\n", idn_result_tostring(r));
>           return 1;
>       }
>       inet_ntop(hp->h_addrtype, hp->h_addr, addr, sizeof(addr));
>       printf("%s %s\n", addr,  buf2);
>       return 0;
>     }