裁剪 libiconv, 让 OpenWrt 支持 GB2312/GBK
iconv库可用于不同字符集之间的转换,比如将 UTF-8 转成 GB2312。在 OpenWrt 中,有libiconv 和 libiconv-full 库,位于:
qca/feeds/packages/libs/libiconv
qca/feeds/packages/libs/libiconv-full
其中 libiconv 是精简版的,libiconv-full 则覆盖了所有的字符集,但是仅仅使用 libiconv-full 库还不能正常的转换中文字符,因为默认情况下 OpenWrt 通过补丁(patch)裁剪掉了很多字符集,其中就包括 GBK/GB2312,所以还需要通过配置才能使用。
在程序中使用iconv,首先,先通过 make menuconfig 打开 libiconv-full,执行:
make menuconfig -> 选择 Libraries -> 选中 libiconv-full
在自己的 Makefile 中引用 libiconv 相关宏,这些宏位于 include/nls.mk 文件中,nls.mk 的内容如下所示:
#
# Copyright (C) 2011-2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
# iconv full
ifeq ($(CONFIG_BUILD_NLS),y)
ICONV_PREFIX:=$(STAGING_DIR)/usr/lib/libiconv-full
ICONV_FULL:=1
INTL_PREFIX:=$(STAGING_DIR)/usr/lib/libintl-full
INTL_FULL:=1
# iconv stub
else
ICONV_PREFIX:=$(STAGING_DIR)/usr/lib/libiconv-stub
ICONV_FULL:=
INTL_PREFIX:=$(STAGING_DIR)/usr/lib/libintl-stub
INTL_FULL:=
endif
PKG_CONFIG_DEPENDS += CONFIG_BUILD_NLS
PKG_BUILD_DEPENDS += !BUILD_NLS:libiconv !BUILD_NLS:libintl
ICONV_DEPENDS:=+BUILD_NLS:libiconv-full
ICONV_CFLAGS:=-I$(ICONV_PREFIX)/include
ICONV_CPPFLAGS:=-I$(ICONV_PREFIX)/include
ICONV_LDFLAGS:=-L$(ICONV_PREFIX)/lib
INTL_DEPENDS:=+BUILD_NLS:libintl-full
INTL_CFLAGS:=-I$(INTL_PREFIX)/include
INTL_CPPFLAGS:=-I$(INTL_PREFIX)/include
INTL_LDFLAGS:=-L$(INTL_PREFIX)/lib
TARGET_CFLAGS += $(ICONV_CFLAGS) $(INTL_CFLAGS)
TARGET_CPPFLAGS += $(ICONV_CFLAGS) $(INTL_CPPFLAGS)
TARGET_LDFLAGS += $(ICONV_LDFLAGS) $(INTL_LDFLAGS)
其中需要用到的有 ICONV_DEPENDS, ICONV_LDFLAGS,ICONV_CFLAGS,在自己的Makefile 中添加,如:
...
include $(INCLUDE_DIR)/nls.mk
...
DEPENDS:= $(ICONV_DEPENDS)
...
LDFLAGS += $(ICONV_LDFLAGS) -liconv
CFLAGS += -I$(ICONV_CFLAGS)
注意到 nls.mk 文件第一条语句
# iconv full
ifeq ($(CONFIG_BUILD_NLS),y)
说明需要先定义 CONFIG_BUILD_NLS 才能使用 iconv-full,该定义需要通过 make menuconfig,执行:
make menuconfig -> 选择 Global build settings -> 选中 Compile with full language support
保存退出后打开 .config 文件,可发现添加了以下配置:
CONFIG_BUILD_NLS=y
...
CONFIG_PACKAGE_libiconv-full=y
此时,在 libiconv-full 默认可以使用的字符集内已经可以进行字符集之间的转换了,比如 UTF-8 -> ASCII,编译并安装:
make package/libiconv-full/{clean,compile,install}
或全编译安装:
make -j8
编译完成后会新增目录:
package/feeds/packages/libiconv-full
现在 libiconv-full 还不支持 GBK/GB2312,因为被 libiconv-full 的补丁 strip 了,该 patch 位于:
package/feeds/packages/libiconv-full/patch/100-strip_charsets.patch
该 patch 去除了大部分的字符集,除了中文,还包括日文,韩文等,可根据需要裁剪。当然,最直接的就是删除整个 patch(切记先备份),删除后重新编译,会发现已经支持 GB2312/GBK 了:
rm package/feeds/packages/libiconv-full/patch/100-strip_charsets.patch
make package/{clean,compile,install}
make -j8
此时在build_dir/target-*/
目录下会生成libiconv-1.11.1
目录(版本号不一定相同)。因为它是支持全字符集的,具有一定的参考价值,可以进行备份(下面会用到),这里只需要备份libiconv-1.11.1/lib
目录即可。
因为支持所有字符集后,整个库将变得很大,这对小容量 Flash 的系统来说是个大灾难。在这里只让它支持GBK/GB2312即可,即不删除整个 patch,而是对它进行修改。
OpenWrt 使用 quilt 工具管理 patch,所以未安装的先安装 quilt:
sudo apt-get install quilt
关于quilt 的配置和 OpenWrt下使用 quilt 管理 patch 的详情可参考官方文档:
接下来更新补丁 100-strip_charsets.patch:
make package/libiconv-full/{clean,prepare} V=s QUILT=1
cd build_dir/target-*/libiconv-*
quilt series
quilt push 100-strip_charsets.patch
执行命令 quilt push 会列出该补丁修改了哪些文件,还可以通过以下命令查看:
quilt files
可以看到下列文件被修改了:
lib/aliases.gperf
lib/aliases.h
lib/aliases_dos.h
lib/canonical.h
lib/canonical_dos.h
lib/canonical_local.h
lib/converters.h
lib/encodings.def
lib/encodings_dos.def
lib/loop_unicode.h
再通过命令 quilt diff 查看补丁更新的详情,可根据所需字符集(关键字)过滤结果:
quilt diff | grep -E "GBK|gbk|gb2312|GB2312|EUCCN|EUC-CN"
得到以下输出,前面的符号 “-/+” 表示该语句通过补丁被“删除/添加”:
-GB_2312-80, ei_gb2312
-ISO-IR-58, ei_gb2312
-CSISO58GB231280, ei_gb2312
-CHINESE, ei_gb2312
-EUC-CN, ei_euc_cn
-EUCCN, ei_euc_cn
-GB2312, ei_euc_cn
-CSGB2312, ei_euc_cn
-GBK, ei_ces_gbk
- char stringpool_str183[sizeof("GB2312")];
- char stringpool_str426[sizeof("GBK")];
- char stringpool_str483[sizeof("CSGB2312")];
- char stringpool_str561[sizeof("CSISO58GB231280")];
- "GB2312",
- "EUCCN",
- "EUC-CN",
- "GBK",
- "CSGB2312",
- "CSISO58GB231280",
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str91, ei_gb2312},
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str222, ei_gb2312},
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str345, ei_gb2312},
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str426, ei_ces_gbk},
- {(int)(long)&((struct stringpool_t *)0)->stringpool_str561, ei_gb2312},
-#include "gb2312.h"
-#include "gbk.h"
-#include "ces_gbk.h"
- "csISO58GB231280", /* IANA */
- /*"GB2312.1980-0", X11R6.4 */
- gb2312,
- { gb2312_mbtowc, NULL }, { gb2312_wctomb, NULL })
-DEFENCODING(( "EUC-CN", /* glibc */
- "EUCCN", /* glibc */
- "GB2312", /* IANA */
- "csGB2312", /* IANA */
-DEFENCODING(( "GBK", /* IANA, JDK 1.1 */
- ces_gbk,
- { ces_gbk_mbtowc, NULL }, { ces_gbk_wctomb, NULL })
接下来就是要恢复GBK/GB2312被删除的部分,具体删除了哪些内容可以对比之前备份的去掉整个patch后生成的源码。修改patch使用命令quilt edit 'file name'
。
(1)quilt edit lib/aliases.gperf ,在语句MS-EE, ei_cp1250
后添加:
GB_2312-80, ei_gb2312
ISO-IR-58, ei_gb2312
CSISO58GB231280, ei_gb2312
CHINESE, ei_gb2312
EUC-CN, ei_euc_cn
EUCCN, ei_euc_cn
GB2312, ei_euc_cn
CN-GB, ei_euc_cn
CSGB2312, ei_euc_cn
GBK, ei_ces_gbk
(2)修改 lib/aliases.h,该文件是由 gperf 工具根据 lib/aliases.gperf 文件生成的,没有安装的先安装该工具:
sudo apt-get install gperf
至于这里如何使用,lib/aliases.h 文件开头的注释已经说明了,使用命令如下覆盖旧文件:
gperf -m 10 lib/aliases.gperf > lib/aliases.h
更多关于 gperf 工具的使用可参考: 使用 gperf 实现高效的 C/C++ 命令行处理
(3)quilt edit lib/converters.h,在最后添加:
typedef struct {
unsigned short indx; /* index into big table */
unsigned short used; /* bitmask of used entries */
} Summary16;
#include "gb2312.h"
#include "gbk.h"
#include "euc_cn.h"
#include "ces_gbk.h"
(4)quilt edit lib/encodings.def,在最后添加:
DEFENCODING(( "GB_2312-80", /* IANA */
"ISO-IR-58", /* IANA */
"csISO58GB231280", /* IANA */
"CHINESE", /* IANA */
/*"GB2312.1980-0", X11R6.4 */
),
gb2312,
{ gb2312_mbtowc, NULL }, { gb2312_wctomb, NULL })
DEFENCODING(( "EUC-CN", /* glibc */
"EUCCN", /* glibc */
"GB2312", /* IANA */
"CN-GB", /* RFC 1922 */
"csGB2312", /* IANA */
/*"EUC_CN", JDK 1.1 */
/*"CP51936", Windows */
),
euc_cn,
{ euc_cn_mbtowc, NULL }, { euc_cn_wctomb, NULL })
DEFENCODING(( "GBK", /* IANA, JDK 1.1 */
),
ces_gbk,
{ ces_gbk_mbtowc, NULL }, { ces_gbk_wctomb, NULL })
(5)quilt edit lib/loop_unicode.h,去除对unicode_transliterate()
函数体的注释,并将函数体中所有的符号/-*
和*-/
恢复成正常注释/*
和*/
(6)由于(5)中的unicode_transliterate()
会调用函数johab_hangul_decompose()
,该函数位于 lib/converters.h中,再次修改 lib/converters.h 文件,添加:
#include "johab_hangul.h"
至此,修改完成,再次通过quilt diff
命令可以发现被删除的有关GB2312/GBK的部分已经恢复。但是还需要更新补丁修改才能最终完成:
quilt refresh
退回到编译目录, 重新编译:
cd ../../..
make package/libiconv-full/update V=s
make package/libiconv-full/{clean,compile} package/index V=s
make package/libiconv-full/install
在编译过程中,如果出现很多警告,并最终编译错误,请勿慌张,找到编译输出信息的第一个error,一般就知道错在哪了,然后根据上述 patch 的修改方法修改之。