Hãy Hỗ Trợ Tiếng Trung Cho Bảng Điều Khiển Của Erlang - nói dối e blog

Hãy Hỗ Trợ Tiếng Trung Cho Bảng Điều Khiển Của Erlang

Việc hỗ trợ tiếng Trung Quốc trên bảng điều khiển Erlang

Món quà sách từ Amazon thật sự khiến tôi bất ngờ về tốc độ giao hàng. Ban đầu tôi nghĩ đơn hàng sẽ đến vào thứ Tư, nhưng không ngờ hôm qua đã nhận được. Phải công nhận dịch vụ giao hàng của họ rất đáng khen.

Tôi vừa nhận cuốn “Lập trình Erlang” và dành cả tối hôm qua đọc được khoảng 1/3 nội dung. Hôm nay đã bắt tay vào thực hành ngay. Tuy nhiên, khi sử dụng Erlang (phiên bản 5.6.5) được cài đặt qua apt trên Ubuntu, tôi phát hiện ra shell không hỗ trợ hiển thị tiếng Trung Quốc. Điều này khiến tôi khá bực bội. (T_T)

Vì Erlang là phần mềm mã nguồn mở, nên tôi quyết định nghiên cứu trực tiếp mã nguồn để tìm nguyên nhân. Môi trường của tôi đang sử dụng định dạng UTF-8, nhưng khi nhập chữ Hán, shell Erlang lại tự động chuyển chúng thành các số bát phân dạng \xxx. Trong cuốn sách “Lập trình Erlang” (trang 21, mục 2.11) có đề cập: “Đây thực chất là vấn đề liên quan đến thiết lập bộ ký tự và vùng miền của thiết bị đầu cuối hiển thị”. Dù đã cố gắng thay đổi các thiết lập nhưng vẫn không thể hiển thị được chữ Hán, khiến tôi nghi ngờ đây không phải lỗi do cấu hình của mình.

Đáng chú ý, trên máy Windows của đồng nghiệp, shell Erlang lại có thể hiển thị tiếng Trung Quốc bình thường. Điều này càng khiến tôi quyết tâm nghiên cứu mã nguồn để vừa giải quyết vấn đề vừa tìm hiểu phong cách lập trình đặc trưng của Erlang.

Quá trình nghiên cứu không hề dễ dàng. Tôi đã đi qua nhiều ngõ cụt khi tìm kiếm các đoạn mã có vẻ ảnh hưởng đến đầu ra, nhưng sửa đổi đều không mang lại hiệu quả. Ví dụ như trong tệp erts/emulator/beam/erl_printf_term.c tồn tại một macro IS_CNTRL được hard code cho bảng mã LATIN1. Những đoạn mã liên quan đến chuyển đổi ký tự này rất thú vị để nghiên cứu.

Sau khi tìm hiểu sơ qua các đoạn mã C底层 của Erlang, tôi nhận thấy chất lượng mã tương đối tốt, tuy nhiên so với Lua thì chưa thực sự chặt chẽ về tính di động và tiêu chuẩn ngôn ngữ C. Đặc biệt, Lua không có các đoạn mã được thiết kế riêng cho bảng mã cụ thể nào. Tất nhiên, đây chỉ là cảm nhận ban đầu sau vài giờ nghiên cứu - có thể còn mang tính chủ quan.

Cuối cùng tôi đã tìm ra hai điểm mấu chốt cần sửa đổi:

Điểm thứ nhất nằm trong tệp lib/stdlib/src/io_lib.erl. Khi mở tệp này, bạn sẽ thấy phần đầu có ghi chú về ISO 8859-1/Latin-1. Nếu tìm kiếm kỹ, sẽ thấy nó xác định các ký tự từ $\240 đến $\377 là có thể in được. Tuy nhiên với mã hóa UTF-8, phạm vi này chưa đủ. Chỉ cần thay $\240 thành $\200 là được.

Trong shell Erlang, chỉ khi toàn bộ các số nguyên trong mảng đều là ký tự có thể in được, chúng mới được hiển thị dưới dạng “xxxxx”. Ngược lại sẽ hiện thị dưới dạng [xxx,xxx,xxx,xxx]. Sau khi sửa đổi này, các chuỗi tiếng Trung Quốc mã hóa UTF-8 sẽ không còn bị chuyển đổi thành dãy số nữa.

Điểm thứ hai nằm trong mã C.

Do Erlang tự quản lý nhiều tiến trình (không phải tiến trình hệ điều hành), để thống nhất việc nhập/xuất dữ liệu, thực tế các thao tác IO đều được tuần tự hóa thông qua một tiến trình duy nhất. Vì vậy, khi sử dụng hàm io:format, thực chất là đang gửi một thông điệp đến tiến trình quản lý IO đó.

Để tìm ra tiến trình xử lý IO cuối cùng, tôi đã nghiên cứu mã nguồn Erlang trong thư mục lib/kernel/src. Cuối cùng phát hiện ra module user chịu trách nhiệm tổng hợp, sau đó giao cho user_drv xử lý IO.

Tuy nhiên, chúng ta đều biết Erlang không thể trực tiếp thực hiện thao tác IO, mà phải thông qua mã C tương tác với hệ điều hành. Trong user_drv tồn tại một thành phần gọi là tty_sl - đây chính là phần xử lý đầu cuối được viết bằng C.

Cách Erlang tương tác với các ngôn ngữ khác rất đặc biệt - thông qua cơ chế truyền thông điệp giữa các tiến trình (không nhất thiết là tiến trình hệ điều hành), sử dụng luồng dữ liệu nhị phân. Điều này rất giống với kiến trúc đa tiến trình trong hệ thống máy chủ game của chúng tôi. Phải thừa nhận rằng Erlang đã thực hiện điều này một cách rất tinh tế. Vì thời gian có hạn hôm nay xin phép không đi sâu vào chủ đề này.

Quay lại vấn đề chính, mã nguồn tty_sl nằm trong tệp /erts/emulator/drivers/unix/ttsl_drv.c. Đọc qua tệp nguồn này sẽ hiểu cách module C tương tác với Erlang.

Vấn đề hiển thị tiếng Trung Quốc nằm ở đoạn mã hard code xử lý các ký tự có giá trị >= 128 - chúng bị chuyển đổi thành dạng \xxx bát phân. Tôi đã đơn giản thêm một macro ISPRINT thay thế cho hàm isprint, cho phép các ký tự >= 128 trả về giá trị true. Sau khi biên dịch và cài đặt lại, shell Erlang đã có thể hiển thị tiếng Trung Quốc đúng trên Ubuntu.

Tuy nhiên vẫn còn một vấn đề nhỏ: xử lý con trỏ chưa chính xác, ví dụ như phím Backspace có thể xóa chỉ một nửa ký tự tiếng Trung. Theo tôi nghĩ, cách sửa đơn giản và ổn định nhất là thay đổi mã nội bộ của dịch vụ tty thành UCS-2, như vậy mỗi ký tự tiếng Trung sẽ được xử lý như một đơn vị nguyên vẹn. Tất nhiên, cũng có thể tìm cách xử lý chính xác mã UTF-8.

Vấn đề này xin để lại cho các nghiên cứu sau này.

0%