目录
项目简介
bcal (Byte CALculator) 是一个 REPL(Read-Eval-Print Loop)命令行工具,用于存储表达式计算、SI/IEC 单位转换、字节地址计算、进制转换和 LBA/CHS 计算。对于经常处理位、字节、地址和二进制前缀的开发者来说非常有用。
主要特性
- REPL 和单次执行模式:支持交互式和命令行参数两种使用方式
- 存储单位表达式计算:支持 KiB、MiB、GiB、TiB、kB、MB、GB、TB 等单位
- 通用计算器模式:可以使用
bc或calc进行通用数值计算 - 管道输入支持:支持从管道或文件重定向输入
- IEC/SI 标准转换:支持二进制和十进制前缀转换
- 交互模式:存储最后一次有效结果供重复使用
- 地址显示:以字节、LBA:OFFSET 格式显示地址
- CHS/LBA 转换:支持 CHS 到 LBA 的相互转换
- 进制转换:支持二进制、十进制和十六进制转换
- 自定义参数:可自定义扇区大小、最大磁头数/柱面和最大扇区/磁道
bcal 工具介绍
安装方式
从源码安装
git clone https://github.com/jarun/bcal.git
cd bcal
make strip install
依赖项
- 标准 C 库:libc
- GNU Readline 或 BSD Editline:用于交互式输入(可选)
- GNU bc 或 calc:用于通用数值计算(可选)
基本使用
命令行选项
usage: bcal [-c N] [-f loc] [-s bytes] [expr]
[N [unit]] [-b [expr]] [-m] [-d] [-h]
Storage expression calculator.
positional arguments:
expr expression in decimal/hex operands
N [unit] capacity in B/KiB/MiB/GiB/TiB/kB/MB/GB/TB
default unit is B (byte), case is ignored
N can be decimal or '0x' prefixed hex value
optional arguments:
-c N show +ve integer N in binary, decimal, hex
-f loc convert CHS to LBA or LBA to CHS
-s bytes sector size [default 512]
-b [expr] enter bc mode or evaluate expression in bc
-m show minimal output (e.g. decimal bytes)
-d enable debug information and logs
-h show this help
使用示例
- 计算存储表达式:
$ bcal "(5kb+2mb)/3"
$ bcal "5 tb / 12"
$ bcal "(2giB * 2) / (2kib >> 2)"
- 单位转换:
$ bcal 20140115 b
$ bcal 0x1335053 B
$ bcal 0xaabbcc kb
$ bcal 0xdef Gib
- LBA/CHS 转换:
$ bcal -f l500
$ bcal -f c10-10-10
- 进制转换:
$ bcal -c 20140115
$ bcal -c 0b1001100110101000001010011
$ bcal -c 0x1335053
- 交互模式:
$ bcal
bcal> 15 gib + 15 kib
bcal> r / 5
bcal> ?
适配 HarmonyOS 的准备工作
1. 项目结构
code/bcal/
├── build_ohos.sh # HarmonyOS 构建脚本
├── hnp.json # HNP 包配置文件
├── Makefile # 构建文件
├── src/
│ ├── bcal.c # 主程序源码
│ └── readline_stub.h # readline 替代实现
├── inc/ # 头文件目录
└── README.md # 项目说明
2. HNP 配置文件
创建 hnp.json 文件:
{
"type":"hnp-config",
"name":"bcal",
"version":"2.4.0",
"install":{}
}
3. 构建脚本框架
创建 build_ohos.sh 脚本,参考其他项目的构建脚本结构:
#!/bin/bash
# bcal HarmonyOS 构建脚本
export BCAL_INSTALL_HNP_PATH=${HNP_PUBLIC_PATH}/bcal.org/bcal_2.4.0
sys_prefix=${PREFIX}
export PREFIX=${BCAL_INSTALL_HNP_PATH}
echo "${PREFIX}"
# 创建安装目录
mkdir -p ${BCAL_INSTALL_HNP_PATH}
# 构建和安装
make clean
make VERBOSE=1
make install
# 复制 HNP 配置文件
cp hnp.json ${BCAL_INSTALL_HNP_PATH}/
# 打包
pushd ${BCAL_INSTALL_HNP_PATH}/../
${HNP_TOOL} pack -i ${BCAL_INSTALL_HNP_PATH} -o ${ARCHIVE_PATH}/
tar -zvcf ${ARCHIVE_PATH}/ohos_bcal_2.4.0.tar.gz bcal_2.4.0/
popd
export PREFIX=${sys_prefix}
构建脚本实现
完整的 build_ohos.sh
#!/bin/bash
# ============================================================================
# build_ohos.sh - bcal 工具 HarmonyOS 构建脚本
# ============================================================================
# 设置 HNP 安装路径
export BCAL_INSTALL_HNP_PATH=${HNP_PUBLIC_PATH}/bcal.org/bcal_2.4.0
# 保存并临时修改 PREFIX
sys_prefix=${PREFIX}
export PREFIX=${BCAL_INSTALL_HNP_PATH}
echo "${PREFIX}"
# 创建安装目录(确保目录存在)
mkdir -p ${BCAL_INSTALL_HNP_PATH}
# 清理之前的构建产物
make clean
# 编译时禁用 readline(使用 stub 实现)
# HarmonyOS SDK 中没有 readline 库,需要使用自定义的 stub 实现
make VERBOSE=1 CPPFLAGS="-UHAVE_READLINE" LDLIBS_READLINE="" || {
echo "Error: make failed"
exit 1
}
# 安装
make install || {
echo "Error: make install failed"
exit 1
}
# 复制 HNP 配置文件
cp hnp.json ${BCAL_INSTALL_HNP_PATH}/ || {
echo "Error: failed to copy hnp.json"
exit 1
}
# 打包
if [ -d "${BCAL_INSTALL_HNP_PATH}" ]; then
pushd ${BCAL_INSTALL_HNP_PATH}/../ > /dev/null
${HNP_TOOL} pack -i ${BCAL_INSTALL_HNP_PATH} -o ${ARCHIVE_PATH}/ || {
echo "Error: HNP pack failed"
popd > /dev/null
export PREFIX=${sys_prefix}
exit 1
}
tar -zvcf ${ARCHIVE_PATH}/ohos_bcal_2.4.0.tar.gz bcal_2.4.0/ || {
echo "Error: tar failed"
popd > /dev/null
export PREFIX=${sys_prefix}
exit 1
}
popd > /dev/null
else
echo "Error: Installation directory does not exist: ${BCAL_INSTALL_HNP_PATH}"
export PREFIX=${sys_prefix}
exit 1
fi
# 恢复 PREFIX
export PREFIX=${sys_prefix}
遇到的问题与解决方案
问题一:clang-15 链接器参数语法变更
错误现象
clang-15: error: '-fuse-ld=' taking a path is deprecated; use '--ld-path=' instead [-Werror,-Wfuse-ld-path]
问题分析
clang-15 编译器不再支持 -fuse-ld=/path/to/ld 的语法,需要使用新的 --ld-path=/path/to/ld 语法。
解决方案
在 build.sh 中修改 CFLAGS 和 LDFLAGS:
修改前:
export CFLAGS="-fPIC -D__MUSL__=1 -D__OHOS__ -fstack-protector-strong --target=${TARGET_PLATFORM} -fuse-ld=${LD} --sysroot=${SYSROOT}"
export LDFLAGS="${LDFLAGS} -fuse-ld=${LD} --target=${TARGET_PLATFORM} --sysroot=${SYSROOT}"
修改后:
export CFLAGS="-fPIC -D__MUSL__=1 -D__OHOS__ -fstack-protector-strong --target=${TARGET_PLATFORM} --ld-path=${LD} --sysroot=${SYSROOT}"
export LDFLAGS="${LDFLAGS} --ld-path=${LD} --target=${TARGET_PLATFORM} --sysroot=${SYSROOT}"
问题二:readline 库缺失
错误现象
src/bcal.c:31:10: fatal error: 'readline/history.h' file not found
#include <readline/history.h>
^~~~~~~~~~~~~~~~~~~~
问题分析
HarmonyOS SDK 的 sysroot 中没有 readline 库。bcal 默认依赖 readline 用于交互式输入,但该功能在 HarmonyOS 环境中不可用。
解决方案
创建 readline 的 stub 实现,使代码可以在没有 readline 的情况下编译:
1. 创建 src/readline_stub.h:
#ifndef READLINE_STUB_H
#define READLINE_STUB_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Simple readline stub implementation for systems without readline
static inline char *readline(const char *prompt) {
if (prompt) {
fputs(prompt, stdout);
fflush(stdout);
}
char *line = NULL;
size_t len = 0;
ssize_t read = getline(&line, &len, stdin);
if (read == -1) {
free(line);
return NULL;
}
// Remove trailing newline
if (read > 0 && line[read - 1] == '\n') {
line[read - 1] = '\0';
}
return line;
}
static inline void add_history(const char *line) {
// Stub: do nothing
(void)line;
}
static inline int read_history(const char *filename) {
// Stub: do nothing
(void)filename;
return 0;
}
static inline int write_history(const char *filename) {
// Stub: do nothing
(void)filename;
return 0;
}
#endif // READLINE_STUB_H
2. 修改 src/bcal.c 使用条件编译:
#include <getopt.h>
#if defined(HAVE_READLINE) && HAVE_READLINE == 1
#include <readline/history.h>
#include <readline/readline.h>
#else
#include "readline_stub.h"
#endif
#include "dslib.h"
#include "log.h"
3. 处理 readline 特定函数调用:
#if defined(HAVE_READLINE) && HAVE_READLINE == 1
rl_bind_key('\t', rl_insert);
#else
// Tab completion not available without readline
#endif
4. 在构建脚本中禁用 readline:
make VERBOSE=1 CPPFLAGS="-UHAVE_READLINE" LDLIBS_READLINE=""
问题三:构建脚本错误处理不完善
错误现象
cp: directory /Users/jianguo/HarmonyOSPC/build/data/service/hnp/bcal.org/bcal_2.4.0 does not exist
pushd: /Users/jianguo/HarmonyOSPC/build/data/service/hnp/bcal.org/bcal_2.4.0/../: No such file or directory
popd: directory stack empty
问题分析
构建脚本缺少错误处理和目录检查,导致在编译失败时后续步骤无法正确执行。
解决方案
添加完善的错误处理和目录检查:
# 创建安装目录(确保目录存在)
mkdir -p ${BCAL_INSTALL_HNP_PATH}
# 添加错误处理
make VERBOSE=1 CPPFLAGS="-UHAVE_READLINE" LDLIBS_READLINE="" || {
echo "Error: make failed"
exit 1
}
make install || {
echo "Error: make install failed"
exit 1
}
# 打包前检查目录是否存在
if [ -d "${BCAL_INSTALL_HNP_PATH}" ]; then
pushd ${BCAL_INSTALL_HNP_PATH}/../ > /dev/null
# ... 打包操作 ...
popd > /dev/null
else
echo "Error: Installation directory does not exist"
exit 1
fi
构建结果验证
构建输出
成功构建后,会在 output/ 目录下生成以下文件:
bcal.hnp- HarmonyOS Native Package 格式包(约 45KB)ohos_bcal_2.4.0.tar.gz- tar.gz 压缩包(约 45KB)
验证命令
# 检查文件是否存在
ls -lh output/bcal.hnp output/ohos_bcal_2.4.0.tar.gz
# 检查 HNP 文件格式
file output/bcal.hnp
# 查看 tar.gz 内容
tar -tzf output/ohos_bcal_2.4.0.tar.gz
安装目录结构
bcal_2.4.0/
├── bin/
│ └── bcal # 可执行文件
├── hnp.json # HNP 配置文件
└── share/
├── doc/
│ └── bcal/
│ └── README.md
└── man/
└── man1/
└── bcal.1.gz # 手册页
终于编译成功了

使用示例
基本计算
# 计算存储表达式
$ bcal "(5kb+2mb)/3"
$ bcal "5 tb / 12"
$ bcal "(2giB * 2) / (2kib >> 2)"
# 单位转换
$ bcal 20140115 b
$ bcal 0x1335053 B
$ bcal 0xaabbcc kb
LBA/CHS 转换
# LBA 转 CHS
$ bcal -f l500
$ bcal -f l0x600-18-0x7e
# CHS 转 LBA
$ bcal -f c10-10-10
$ bcal -f c0x10-0x10-0x10
进制转换
# 显示二进制、十进制和十六进制
$ bcal -c 20140115
$ bcal -c 0b1001100110101000001010011
$ bcal -c 0x1335053
交互模式
$ bcal
bcal> 15 gib + 15 kib
bcal> r / 5
bcal> b # 切换到 bc 模式
bcal> ? # 显示帮助
bcal> q # 退出
管道输入
# 从管道输入
$ printf '15 kib + 15 gib \n r / 5' | bcal -m
# 从文件输入
$ cat expr
15 gib + 15 kib
r / 5
$ bcal -m < expr
总结
适配要点
- 链接器参数更新:使用
--ld-path=替代已弃用的-fuse-ld=语法 - readline 依赖处理:创建 stub 实现,使代码可以在没有 readline 的情况下编译
- 错误处理完善:添加完善的错误检查和错误处理机制
- 目录管理:确保安装目录在构建前已创建
技术亮点
- 条件编译:使用条件编译实现 readline 的可选依赖
- Stub 模式:通过 stub 实现保持代码兼容性
- 错误恢复:完善的错误处理确保构建过程的可靠性
适用场景
bcal 工具特别适用于:
- 存储系统开发:计算存储容量、地址转换
- 文件系统开发:LBA/CHS 转换
- 系统管理:存储单位转换和计算
- 嵌入式开发:处理二进制前缀和地址计算

被折叠的 条评论
为什么被折叠?



