mandoc 1.14.5 移植到 SerenityOS 全解析:九个补丁如何补齐 glob、正则与构建链
mandoc 1.14.5 移植到 SerenityOS 全解析九个补丁如何补齐 glob、正则与构建链【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity本文以 Ports/mandoc/patches/ReadMe.md 为主线逐补丁拆解 mandocBSD 系手册阅读与排版工具移植到 SerenityOS 的完整过程从引入 NetBSD 版 glob(3) 兼容实现、用 PCRE2 顶替系统正则接口到改造 configure 脚本与构建系统。读完本文你将理解一个跨平台 C 项目落进 SerenityOS Ports 体系所需的典型兼容性改造手段并能直接复用到其他第三方软件的移植工作中。mandoc 与 SerenityOS 的 Ports 移植体系mandoc 是 OpenBSD 项目维护的手册页man page解析与排版工具集负责把 mdoc(7)、man(7) 格式的源文件渲染为终端可读或 HTML 形式并配套提供man、apropos等手册检索命令。要让它在 SerenityOS 上运行需要走该系统的第三方软件移植框架每个 Port 用一个package.sh脚本描述源码下载地址、版本、构建选项与依赖关系SerenityOS 的移植工具链../.port_include.sh会据此自动完成下载、打补丁、配置、编译与安装。以 mandoc 为例package.sh 的关键信息如下portmandoc version1.14.5 useconfiguretrue files( https://mandoc.bsd.lv/snapshots/mandoc-${version}.tar.gz#8219b42cb56fc07b2aa660574e6211ac38eefdbf21f41b698d3348793ba5d8f7 ) depends(less pcre2 zlib)useconfiguretrue源码包自带configure脚本需要执行配置步骤depends声明了三个运行时/链接期依赖 ——less分页显示、pcre2正则引擎、zlib压缩库对应 Ports/less、Ports/pcre2、Ports/zlib 三个 Portfiles中的#后是源码 tarball 的 SHA-256 校验和保证下载内容可验证。mandoc 上游源码面向的是完整 POSIX/BSD 环境而 SerenityOS 当时的手册栈尚未提供全部所需接口。于是就有了 ReadMe.md 记录的 9 个补丁它们被按序号依次打入源码树。九个补丁的总览补丁文件核心动作0001新增compat_glob.c引入一份完整的 glob(3) 兼容实现1174 行0002新增glob.h为上述实现配套声明glob_t结构与全部标志位0003configure让配置脚本识别 Serenity 环境并链接 pcre20004dba_read.cregex.h改为pcre2posix.h0005dbm_map.cregex.h改为pcre2posix.h0006dbm.cregex.h改为pcre2posix.h0007main.c系统glob.h改为仓库内glob.h0008Makefile把compat_glob.c纳入源码与对象列表0009mansearch.c同时修正 glob 与 regex 的头文件引用从补丁头部信息可以看出两批来源0001–0006、0008、0009 由 Brian CallahanOpenBSD 开发者在 2020 年 1 月制作0007 则在 2021 年 12 月由 Daniel Bertalan 参与合著Co-Authored-By说明这份移植是持续维护演进的。补丁 0001/0002为 Serenity 补上一份完整的 glob(3)mandoc 的mansearch子系统在按关键字检索手册页时需要使用 glob 通配符展开路径然而移植时 SerenityOS 尚未提供可用的系统glob(3)因此补丁 0001 直接从 NetBSD 引入了一整份实现。来源与许可compat_glob.c开头的版本信息标明它源自 NetBSD 的glob.crevision 1.392019-05-29作者 christos而代码本身又可追溯回加州大学伯克利分校的 BSD 原始实现原作者是 Guido van RossumPython 之父许可为 BSD-3-Clause。补丁里还通过#define NO_GETPW_R关掉了对可重入getpwuid_r/getpwnam_r的依赖改用传统的getpwnam/getpwuid降低了对 libc 接口的要求。公共 APIglob_t与标志位补丁 0002 新增的glob.h同样源自 NetBSDglob.hv1.27定义了调用方可见的全部接口。核心数据结构如下typedef struct { __gl_size_t gl_pathc; /* Count of total paths so far. */ __gl_size_t gl_matchc; /* Count of paths matching pattern. */ __gl_size_t gl_offs; /* Reserved at beginning of gl_pathv. */ int gl_flags; /* Copy of flags parameter to glob. */ char **gl_pathv; /* List of paths matching pattern. */ int (*gl_errfunc)(const char *, int); /* Alternate filesystem access methods ... */ void (*gl_closedir)(void *); struct dirent *(*gl_readdir)(void *); void *(*gl_opendir)(const char *); int (*gl_lstat)(const char *, __gl_stat_t *); int (*gl_stat)(const char *, __gl_stat_t *); } glob_t;其中gl_pathc/gl_pathv是结果集合路径数与路径数组gl_offs允许在结果头部预留空位而gl_errfunc与那组可替换的目录/文件访问函数指针构成了GLOB_ALTDIRFUNC扩展的挂钩允许调用方注入自定义的opendir/readdir/closedir/stat/lstat实现。标准 POSIX 标志位定义如下#define GLOB_APPEND 0x00001 /* Append to output from previous call. */ #define GLOB_DOOFFS 0x00002 /* Use gl_offs. */ #define GLOB_ERR 0x00004 /* Return on error. */ #define GLOB_MARK 0x00008 /* Append / to matching directories. */ #define GLOB_NOCHECK 0x00010 /* Return pattern itself if nothing matches. */ #define GLOB_NOSORT 0x00020 /* Dont sort. */ #define GLOB_NOESCAPE 0x01000 /* Disable backslash escaping. */在_NETBSD_SOURCE开启时还额外提供一组 BSD 扩展标志标志值含义GLOB_ALTDIRFUNC0x00040使用调用方指定的目录访问函数GLOB_BRACE0x00080按 csh 风格展开{a,b}花括号GLOB_MAGCHAR0x00100模式中确实含通配元字符由实现回填GLOB_NOMAGIC0x00200无元字符时才回填模式本身csh 兼容GLOB_LIMIT0x00400限制匹配内存消耗GLOB_TILDE0x00800展开~user为口令文件中的家目录GLOB_PERIOD0x02000允许元字符匹配文件名开头的点GLOB_NO_DOTDIRS0x04000.与..对通配符隐藏GLOB_STAR0x08000支持**递归目录GLOB_TILDE_CHECK0x10000展开~user且用户不存在时报错错误码同样沿用 BSD 语义GLOB_NOSPACE-1分配失败、GLOB_ABORTED-2不可忽略的错误、GLOB_NOMATCH-3无匹配且未设GLOB_NOCHECK、GLOB_NOSYS-4功能未实现。此外还声明了glob()、globfree()与glob_pattern_p()三个函数。实现要点从模式编译到递归展开compat_glob.c的实现结构清晰值得展开解读模式预编译glob0把用户的字符串模式转换成内部Char数组用高位标记元字符M_ONE对应?、M_ALL对应*、M_SET/M_NOT/M_RNG/M_END对应字符类[...]、M_QUOTE/M_PROTECT对应反斜杠转义并在此过程中折叠连续*注释明确说明这是为了“避免指数级回溯”avoid exponential behavior。遇[时若]不闭合则按普通字符处理!用作取反前缀兼容 SysV、POSIX 与 ksh 的[!...]记法。路径段递归匹配glob2/glob3glob2沿模式逐段拷贝非元字符前缀一旦遇到含元字符的段就转交glob3glob3打开目录、逐项readdir对每个目录项调用match()做模式匹配命中后递归进入下一段。二者互相递归递归深度等于模式中元字符段的个数。可选特性globexp1/globexp2实现GLOB_BRACE的花括号展开递归拆分逗号分隔项globtilde实现GLOB_TILDE的~/~user展开先查$HOME再查口令文件GLOB_STAR支持**递归遍历match()是最终的模式匹配内核处理*、?、字符类与范围*匹配采用经典的“记录下一个回退位置”回溯算法。资源限制当开启GLOB_LIMIT时代码通过struct glob_limit统计各类消耗并对照宏上限防止恶意/超长模式打爆内存#define GLOB_LIMIT_STRING 524288 /* number of readdirs */ #define GLOB_LIMIT_STAT 128 /* number of stat system calls */ #define GLOB_LIMIT_READDIR 65536 /* total buffer size of path strings */ #define GLOB_LIMIT_PATH 1024 /* number of path elements */ #define GLOB_LIMIT_BRACE 128 /* Number of brace calls */结果收集globextend用realloc动态扩张gl_pathv每次追加一条路径并维护gl_pathc无匹配时按GLOB_NOCHECK/GLOB_NOMAGIC决定是否回填原始模式未设GLOB_NOSORT时最后用qsortstrcoll对结果排序。补丁 0003让 configure 认识 Serenitymandoc 自带的configure脚本会做大量特性探测0003 针对 SerenityOS 的环境逐项修正操作系统标识OSNAMESerenity、OSENUMMANDOC_OS_OTHER让生成的版本/OS 字符串正确反映目标平台手册路径MANPATH_BASE与MANPATH_DEFAULT改为/usr/share/man:/usr/local/share/manMANDIR默认值改为${PREFIX}/share/man符合 SerenityOS 的目录布局编译选项CFLAGS-O2 -pipe并用静态探测结果直接预设部分特性避免运行期探测失败——例如HAVE_GETLINE1、HAVE_MKDTEMP1、HAVE_NTOHL1、HAVE_STRNDUP1直接声明 Serenity libc 已具备这些函数而HAVE_PLEDGE0则显式关闭 OpenBSD 的pledge(2)沙箱Serenity 没有该接口删除 fatal 检查移除了对nanosleep的强制探测段原本探测失败会exit 1因为 Serenity 不需要-lrt链接库LDADD追加-lpcre2-posix -lpcre2-8 -lz把正则与压缩依赖显式写入最终链接行——这一步与后面的 pcre2 头文件替换是配套的。补丁 0004–0006用 PCRE2 提供 POSIX 正则接口mandoc 的检索/索引子系统dba_read.c、dbm_map.c、dbm.c都要用到 POSIX 风格的正则 APIregcomp/regexec等。补丁 0004–0006 的文件名标题完全相同“Use pcre2 for regex”分别把这三个文件里的#include regex.h统一替换为#include pcre2posix.hpcre2posix.h是 PCRE2 库自带的 POSIX 兼容层头文件SerenityOS 通过 Ports/pcre2 提供 PCRE2 8-bit 库其兼容层以相同的regex_t/regcomp/regexec/regerror/regfree符号实现了 POSIX 正则接口因此 mandoc 的业务代码无需改动只需把头文件指向该兼容层再配合补丁 0003 中的-lpcre2-posix -lpcre2-8即可完成链接。这种“以第三方库的 POSIX 兼容层填补系统接口空缺”的做法是跨平台移植的常见策略。补丁 0007–0009接入构建链并修正引用前两个补丁引入了 glob 实现但要让代码真正使用它还需三处收尾0007main.c把#include glob.h改为#include glob.h。注意此处的微妙之处main.c原本想用系统 glob 头但 Serenity 当时没有必须显式改为引用仓库内新增的补丁版头文件该补丁晚于其他补丁一年多合入且由 Daniel Bertalan 共同署名印证了移植工作的持续迭代0008Makefile把compat_glob.c加入SRCS变量、把compat_glob.o加入COMPAT_OBJS变量与既有的compat_err.c、compat_fts.c、compat_getline.c、compat_ohash.c等兼容层文件并列——mandoc 的兼容层设计就是“把缺失的系统函数以compat_*文件补齐”0009mansearch.c一处同时修两处——glob.h改为glob.h同上regex.h改为pcre2posix.h同上。mansearch.c是手册检索的核心模块它把 glob 与 regex 两个兼容层真正用了起来。至此九个补丁形成完整闭环先补实现glob.c/glob.h→ 再改配置configure 识别平台并链接 pcre2→ 换正则接口三个 db 相关文件→ 最后接入构建Makefile并修正所有引用点main.c、mansearch.c。从源码看移植的可复用经验从这份补丁集可以提炼出几条对任何“往 SerenityOS 移植 C 项目”都有普适价值的方法论先盘点系统缺口再决定“引入”还是“替换”glob(3) 是系统根本没有的接口所以从 NetBSD 整份引入并以compat_*命名纳入构建与 mandoc 自带的compat_fts.c、compat_ohash.c等既有模式一致regex 则是系统有但实现不完整的接口所以不引入新代码、只把头文件指向 PCRE2 的 POSIX 兼容层业务代码零改动。两者的成本差异很大决策依据就是“缺口到底在哪一层”。configure 脚本是移植主战场第三方软件的configure往往绑定具体发行版行为如 OpenBSD 的pledge、-lrt的nanosleep探测、/usr/X11R6/man这类路径假设。0003 的做法很典型用硬编码预设替代运行期探测删掉不适用平台的 fatal 分支并把平台特有路径、OS 名称、链接库一次性修正。头文件引用的三态glob.h→glob.h系统 → 仓库本地、regex.h→pcre2posix.h系统 → 第三方兼容层、以及补丁 0008 中“只加文件名不加路径”的构建集成三处改动分别对应三种不同的依赖管理方式。兼容层要贴近上游compat_glob.c直接采用 NetBSD 的最新实现并保留其版权头与__RCSID而不是从零重写既保证了行为与 BSD 生态一致也让后续跟随上游修复变得容易。总结mandoc 移植到 SerenityOS 的这九个补丁是一份小而完整的“跨平台兼容性工程”样本ReadMe.md 以极简的补丁清单记录了全部改造而其背后的 compat_glob.c、glob.h、configure 补丁 则揭示了移植的真实复杂度。理解这套“补实现—改配置—换接口—接入构建”的节奏比记住某个具体补丁本身更有价值——它可以直接套用到任何准备落进 Ports 体系的第三方软件上。若想亲自动手只需按 package.sh 中声明的版本与依赖在完整的 SerenityOS 构建环境中执行对应 Port 的构建脚本即可复现整个移植流程。【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考