Skip to content

Commit 9fd2f8a

Browse files
committed
the absence table is top-level, because the nested spelling refuses the manifest
`[c-abi].absent` reads better and is where this was written. Measured against the genuine published 2026.9.18.3 archive -- the index floor at the time -- on the exact manifest openkal-musl 0.17.0 publishes: nested, every mcpp older than this release refuses THE WHOLE MANIFEST on every target with `[c-abi] has no member 'absent'`. The `[c-abi]` parser enumerates its members and rejects the rest, and that strictness is correct: a misspelled `presents` must not silently disable a declaration. In the same measurement an unknown TOP-LEVEL table is ignored and the build completes. Everything this table does is diagnostic. `c_abi_absent_facility_advice` annotates a link that has already failed; no flag, link line or artifact depends on it. So an engine that ignores it produces exactly the raw linker error it produces today, while an engine that refuses it takes the package away entirely and forces the index floor up to this release -- costing every client stopped below that floor the whole index, for a note they merely would not have received. Top-level, openkal-musl 0.17.0 asks nothing of `min_mcpp`. The two spellings are indistinguishable in the parsed result, so no test that reads only the outcome can tell them apart. Three tests state the shape directly: `absent` is not a member of `[c-abi]` and the refusal names the top-level spelling; absences may be stated without an environment declaration, which leaves `declared` false; and the provider gate `[c-abi]` applies is restated rather than inherited, since moving the table moved it out from behind that gate. `prepare` now tests `cAbiDecl->declared` rather than the optional, because a `[c-abi-absent]` table alone sets the optional and has nothing in it for `cenv::realise` to realise.
1 parent 854b99d commit 9fd2f8a

14 files changed

Lines changed: 264 additions & 81 deletions

.agents/docs/2026-09-20-cenv-interfaces-verify.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
# than a hole: `[kernel-abi]` is an unknown top-level table to an older engine
2929
# and is ignored, so a graph that satisfies its requirements builds either way.
3030
# The leg that distinguishes the releases is the refusal. C fails outright on
31-
# the older engine because `[c-abi.absent]` is a new key inside a table it
31+
# the older engine because `[c-abi-absent]` is a new key inside a table it
3232
# knows, where an unrecognised key is a parse error -- the asymmetry docs/22
3333
# records.
3434
set -u
@@ -127,7 +127,7 @@ else
127127
fi
128128

129129
# ── CHANGE 2. A C library states what it does not supply ────────────────────
130-
section "C. [c-abi.absent] is read, and a bad shape is refused (CHANGE)"
130+
section "C. [c-abi-absent] is read, and a bad shape is refused (CHANGE)"
131131
c="$root/c"; rm -rf "$c"; mkdir -p "$c/libc/src" "$c/src"
132132
printf 'int fake_libc_marker(void){return 0;}\n' > "$c/libc/src/lib.c"
133133
printf 'int main(void){return 0;}\n' > "$c/src/main.c"
@@ -158,7 +158,7 @@ presents = "posix"
158158
data-model = "arch-default"
159159
wchar = 32
160160
161-
[c-abi.absent]
161+
[c-abi-absent]
162162
fork = { form = "$1", note = "no process image duplication" }
163163
EOF
164164
}

.agents/docs/2026-09-20-cxa-thread-atexit-finding.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ Windows : __cxa_thread_atexit_impl = 0 -> fallback branch
104104
| 失败在哪 | **链接期** | 运行期 |
105105
| 调用方能否看见 | **能,链接器点名符号** | **不能,注册成功而析构不发生** |
106106
107-
第二种正是 openkal-musl 的 `[c-abi.absent]` 里叫作 `accepted-no-effect` 的那个形状,
107+
第二种正是 openkal-musl 的 `[c-abi-absent]` 里叫作 `accepted-no-effect` 的那个形状,
108108
也是 SPEC §6.1 把「运行期报告不支持」称为缺陷的理由。**一个响亮的构建期失败,
109109
比一个静默的运行期失败好。**
110110

.agents/docs/2026-09-20-ecosystem-execution-plan.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ C openkal-musl(absent + requires)─────┘
3838
|| 仓库 | 内容 | 依赖 |
3939
| --- | --- | --- | --- |
4040
| **E1** | mcpp-index | `pins.toml` runtime 0.10.0 → 0.12.0,重测 30 成员 ||
41-
| **A** | mcpp | P0.1 探针 `--target`、P0.2 注释、P1 冻结语义、P5-L2 解析期集合包含、P7-L3 链接期集合差、`[c-abi.absent]` 解析与诊断 ||
41+
| **A** | mcpp | P0.1 探针 `--target`、P0.2 注释、P1 冻结语义、P5-L2 解析期集合包含、P7-L3 链接期集合差、`[c-abi-absent]` 解析与诊断 ||
4242
| **B** | openkal | `tools/interfaces-from-surface.sh`、README 记述四级阶梯 ||
43-
| **C** | openkal-musl | `[c-abi.absent]` 声明 + CI 断言、`requires-interfaces` | A(字段语义)、B |
43+
| **C** | openkal-musl | `[c-abi-absent]` 声明 + CI 断言、`requires-interfaces` | A(字段语义)、B |
4444
| **D** | openkal-linux / -windows / -macos | `provides-interfaces` 由产物生成 + CI 断言 | B |
4545
| **E2** | mcpp-index | `refused` status、新引擎 pin、重测 | A、E1 |
4646
| **F** | 全部 | 发布、gtc 镜像、沙箱验证 | 全部 |
@@ -104,23 +104,23 @@ libpng、re2、lua(经 capi-lua)。全部是按 `_WIN32` / `__MINGW32__` 选分
104104
`thread_local` 析构的钩子),在环境正确之前到不了。属 openkal-llvm-runtime /
105105
openkal-musl,另轮。
106106

107-
3. **clang 20.1.7 在 Windows 上对三种写法都崩。** `[c-abi.absent]` 的解析块写成
107+
3. **clang 20.1.7 在 Windows 上对三种写法都崩。** `[c-abi-absent]` 的解析块写成
108108
`parse_string` 内的语句块、写成模块导出 purview 里返回
109109
`expected<vector<struct-with-strings>, string>` 的自由函数、以及用成员指针作
110110
sort 投影,在 Windows 上各崩一次,其他宿主全过。最终形态是匿名命名空间里的内部
111111
helper + 出参 + `optional<string>` + 比较器。
112112

113113
4. **两个新键的向后兼容性不同,且这条差别要写进文档。** `[kernel-abi]` 是未知
114-
**顶层表**,旧引擎忽略(实测 2026.9.17.1 静默接受);`[c-abi.absent]` 是已知表里的
114+
**顶层表**,旧引擎忽略(实测 2026.9.17.1 静默接受);`[c-abi-absent]` 是已知表里的
115115
新键,旧引擎拒绝整份清单。因此前者不需要抬 floor,后者需要。
116116

117117
### 4.3 PR
118118

119119
| 仓库 | PR | 内容 |
120120
| --- | --- | --- |
121-
| mcpp | #678 | 探针带目标、撤 hostStripMacros、`[kernel-abi]``[c-abi.absent]`、冻结 `presents` |
121+
| mcpp | #678 | 探针带目标、撤 hostStripMacros、`[kernel-abi]``[c-abi-absent]`、冻结 `presents` |
122122
| openkal | #42 | `check-surface.sh --interfaces/--toml`,接口集由产物派生 |
123-
| openkal-musl | #39 | 0.17.0 `[c-abi.absent]` + CI 断言 |
123+
| openkal-musl | #39 | 0.17.0 `[c-abi-absent]` + CI 断言 |
124124
| openkal-linux | #29 | 0.15.0 `provides-interfaces`,CI 重新生成并 diff |
125125
| mcpp-index | #444 | 抬 pins、重测、`refused` |
126126

.agents/docs/2026-09-20-openkal-c-environment-ecosystem-design.md

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ wchar = 32
574574
builtins = "iso"
575575

576576
# 枚举例外,不枚举规则。form 是该缺席以何种形状到达调用方。
577-
[c-abi.absent]
577+
[c-abi-absent]
578578
fork = { targets = ["*"], form = "link" } # 符号不定义,链接期报
579579
mprotect = { targets = ["*"], form = "enosys" } # 定义存在,报 ENOSYS
580580
sigaction-handler = { targets = ["*"], form = "enosys",
@@ -586,9 +586,27 @@ sigaction-handler = { targets = ["*"], form = "enosys",
586586
1. **CI 可断言。** `form = "link"` 的名字必须**不在**产物的定义里;`form = "enosys"`
587587
的必须**在**。这把一份从来没有执行者的散文变成一条会红的断言。
588588
2. **mcpp 的诊断可以接话。** 链接报 `undefined reference to 'fork'` 时,mcpp 读到
589-
C 库的 `[c-abi.absent]`,补一句「`openkal-musl` 声明 `fork` 在 `x86_64-windows-gnu`
589+
C 库的 `[c-abi-absent]`,补一句「`openkal-musl` 声明 `fork` 在 `x86_64-windows-gnu`
590590
上不可用(link 形式)」,而不是让用户自己去查一份 README。
591591

592+
**这张表是顶层表,而这是量出来的。** 实现时先写成 `[c-abi].absent`——更顺,也更像
593+
它所描述的东西。判据取自**真正发布的 2026.9.18.3 归档**(当时的索引 floor),跑在
594+
openkal-musl 0.17.0 将要发布的那份清单上:嵌套写法让**每个旧 mcpp 在每个目标上拒绝
595+
整份清单**,报 `[c-abi] has no member 'absent'`——`[c-abi]` 的解析器枚举自己的成员并
596+
拒绝其余,而这条严格性本身是对的(拼错的 `presents` 不该静默关掉一条声明)。同一次
597+
测量里,**未知的顶层表被忽略,构建照常完成**。
598+
599+
这个差别决定这张表能不能发。它做的每件事都是诊断性的:给一次**已经失败**的链接加一句
600+
话,没有任何 flag、链接行或产物依赖它。于是忽略它的引擎产出的正是它今天产出的那条链接
601+
错误;而拒绝它的引擎会把索引 floor 逼到 2026.9.20.1——为了一句他们无非是收不到的说明,
602+
夺走停在其下的每个客户端手里的**整个索引**([[index-floor-must-degrade]] 的失败形态)。
603+
改成顶层表后,openkal-musl 0.17.0 **不再要求抬 floor**。
604+
605+
⚠️ 这条差别在解析结果上**看不见**:两种拼法解析出的 `CAbiDecl` 完全相同,任何只读结果
606+
的测试都区分不了。所以判据只能是「拿索引 latest 指向的那个二进制去跑」
607+
([[new-capability-key-floor-measured]]),并且在单测里**直接断言形状**——
608+
`absent` 不是 `[c-abi]` 的成员。
609+
592610
**`form` 这个字段本身承重。** `link` 是 §6.1 要的形状;`enosys` 是需要辩护的例外,
593611
README 今天为每一条都写了辩护(`mprotect`:「musl asks for a guard page ... and
594612
proceeds without one when told this, so the honest answer is also the one it is
@@ -604,8 +622,8 @@ prepared for」)。把辩护变成一个字段,**"有多少例外"就成为
604622
| L2 集合包含 | mcpp 解析期 | 一条规则,引擎不认识任何接口名 |
605623
| L3 未定义符号比对 | mcpp 链接期 | 取对象的未定义符号、求交、映射、比较 |
606624
| L4 | 已有 | — |
607-
| `[c-abi.absent]` 断言 | openkal-musl 的 CI | 一个脚本 |
608-
| `[c-abi.absent]` 诊断接话 | mcpp 链接失败路径 | 一处 |
625+
| `[c-abi-absent]` 断言 | openkal-musl 的 CI | 一个脚本 |
626+
| `[c-abi-absent]` 诊断接话 | mcpp 链接失败路径 | 一处 |
609627

610628
**引擎侧新增两处**(L2 解析、L3 链接),且**都不认识任何具体名字**:L2 做集合包含,
611629
L3 做集合差。名字的含义全部来自 `SURFACE.txt`,而那是 openkal 的 normative 文件,
@@ -618,7 +636,7 @@ L3 做集合差。名字的含义全部来自 `SURFACE.txt`,而那是 openkal
618636
- L3:消费者调用一个**没有声明**的接口,链接期必须报错并指名符号与它所属的接口;
619637
**且在提供该接口的实现上同样报错**——否则这条判据只是重复了 §6.1。
620638
- L4:已由 openkal conformance 覆盖。
621-
- `[c-abi.absent]`:把 `fork` 的 `form` 从 `link` 改成 `enosys`,CI 必须红。
639+
- `[c-abi-absent]`:把 `fork` 的 `form` 从 `link` 改成 `enosys`,CI 必须红。
622640

623641
最后一条与 L3 的第二句是同一个形状:**一条判据必须在"机制本身会通过"的那一侧
624642
也成立,否则它测的是机制不是声明。**
@@ -725,7 +743,7 @@ libarchive,且都在问"平台 SDK / 内核头在不在图里"——正是 P5
725743
| --- | --- |
726744
| 借来的 `__CYGWIN__` | P3 关闭(需重测确认) |
727745
| 空着的解析期格子 | P5 关闭 |
728-
| 探针只铺到宏、没铺到接口 | **P7 关闭**。四级阶梯(L1 发布 / L2 解析 / L3 链接 / L4 运行),材料全部已在仓库里(`SURFACE.txt`、SPEC §9.2、`kal_interfaces()`),引擎侧新增两处且都不认识任何具体名字。C 库一级枚举例外而不枚举规则(`[c-abi.absent]`) |
746+
| 探针只铺到宏、没铺到接口 | **P7 关闭**。四级阶梯(L1 发布 / L2 解析 / L3 链接 / L4 运行),材料全部已在仓库里(`SURFACE.txt`、SPEC §9.2、`kal_interfaces()`),引擎侧新增两处且都不认识任何具体名字。C 库一级枚举例外而不枚举规则(`[c-abi-absent]`) |
729747

730748
**P7 同时改变了"可验证性"这一维的整体评级**:在它之前,`[c-abi]` 只有宏一级被校验,
731749
接口一级完全是信任;在它之后,四个时刻各有一条会红的断言,且 `provides-interfaces`
@@ -739,9 +757,9 @@ libarchive,且都在问"平台 SDK / 内核头在不在图里"——正是 P5
739757
| --- | --- | --- |
740758
| openkal 规范 | 零。不新增也不修改任何 `kal_*` | — |
741759
| openkal 各实现 | P5/P7 要求填 `provides-interfaces`,且**由产物生成而非手写**;CI 增加 L1 断言 | 一次性,完全机械化 |
742-
| openkal-musl | P4 在 `port/` 新增合成表与 conformance 断言;P7 把 README 的"absent"散文表变成 `[c-abi.absent]` 并加 CI 断言 | 新增能力,向后兼容 |
760+
| openkal-musl | P4 在 `port/` 新增合成表与 conformance 断言;P7 把 README 的"absent"散文表变成 `[c-abi-absent]` 并加 CI 断言 | 新增能力,向后兼容 |
743761
| openkal-llvm-runtime | 零 | — |
744-
| mcpp 引擎 | P0 两处修复;P5 新增字段与解析规则;**P7 新增链接期集合差与 `[c-abi.absent]` 诊断接话**;P3 改两个令牌;P1 改文档 | 新增能力,向后兼容 |
762+
| mcpp 引擎 | P0 两处修复;P5 新增字段与解析规则;**P7 新增链接期集合差与 `[c-abi-absent]` 诊断接话**;P3 改两个令牌;P1 改文档 | 新增能力,向后兼容 |
745763
| mcpp-index | P2 拆 `status`;libarchive 那 2 处身份判断改成 P5 声明 | 跟随 |
746764
| 终端用户(用 openkal) | P3 改变 `__CYGWIN__`,该目标上的包重建一次 | 一次破坏性 |
747765
| **不用 openkal 的用户** | **零。命令行逐字节不变** | 已由现有"未声明者不变"保证 |
@@ -751,7 +769,7 @@ libarchive,且都在问"平台 SDK / 内核头在不在图里"——正是 P5
751769
## 9. 顺序
752770

753771
**落地记录见 `2026-09-20-ecosystem-execution-plan.md` §4,自审见
754-
`2026-09-20-wave-self-review.md`。** 本轮落地 P0、P1、P2、P5、P7(L1/L2/`[c-abi.absent]`),
772+
`2026-09-20-wave-self-review.md`。** 本轮落地 P0、P1、P2、P5、P7(L1/L2/`[c-abi-absent]`),
755773
未落地 P3、P4、P6 与 P7 的 L3;每一项未落地的理由与它缺席的后果都写在那两份记录里。
756774

757775

@@ -771,7 +789,7 @@ P6 win-ucrt ──── 需要 c++-abi 侧配套,最后
771789

772790
**P7 的 L1 可以独立先行**:把 `SURFACE.txt` → 接口集的反推脚本与 CI 断言做出来,
773791
不依赖任何引擎改动,而且它产出的正是 P5 需要的那份 `provides-interfaces`。
774-
`[c-abi.absent]` 的 CI 断言同样可以独立先行。
792+
`[c-abi-absent]` 的 CI 断言同样可以独立先行。
775793

776794
P3 **不得**先于重测落地:libarchive 的生成配置头含
777795
`#if defined(_WIN32) && !defined(__CYGWIN__)`,撤销会改变它的分支。
@@ -798,7 +816,7 @@ P3 **不得**先于重测落地:libarchive 的生成配置头含
798816
- **D7 L3(链接期集合差)是否纳入本轮。** 它是 `requires-interfaces` 唯一的校验者,
799817
没有它 P5 的消费者一侧是纯被信任的。代价是 mcpp 链接期多一次符号读取。
800818
**建议:纳入,但可以先做成 warning,一个发布周期后转 error。**
801-
- **D8 `[c-abi.absent]` 的 `form` 取值集。** 本文用 `link` / `enosys` 两个值。
819+
- **D8 `[c-abi-absent]` 的 `form` 取值集。** 本文用 `link` / `enosys` 两个值。
802820
是否需要第三个(例如"接受但无效"——README 记录的 `tcsetattr` 部分字段即是)?
803821
**建议:需要,命名为 `accepted-no-effect`,因为它正是 0.16.0 之前那个真实缺陷的
804822
形状,给它一个名字才能被盯住。**

.agents/docs/2026-09-20-wave-self-review.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ status: active
2323
| P4 合成节点 | 未做 | 独立工作量 |
2424
| P5 接口列举 | mcpp#678 + openkal#42 + openkal-linux#29 + openkal-windows#26 | 设计说"引擎新增两处",实际只新增解析期一处:链接期那一处(L3)未做 |
2525
| P6 `openkal-win-ucrt` | 未做 | 需 c++-abi 配套 |
26-
| P7 校验阶梯 | L1 + L2 + `[c-abi.absent]` 落地;L3 未做 | 见下 |
26+
| P7 校验阶梯 | L1 + L2 + `[c-abi-absent]` 落地;L3 未做 | 见下 |
2727

2828
**与设计最大的偏差是 P7 的 L3(链接期集合差)。** 设计把它列为"`requires-interfaces` 唯一
2929
的校验者",本轮没有做。后果要写清楚:**消费者一侧的声明目前仍然只被信任。** 一个包可以
@@ -36,15 +36,15 @@ status: active
3636

3737
| 维度 | 核查 | 结论 |
3838
| --- | --- | --- |
39-
| **架构** | 新增机制是否落在 §6.2 的三时刻表内 | 是。`requires/provides-interfaces` 在解析期,`[c-abi.absent]``link` 形状在链接期,`enosys` 在运行期。没有一项落在预处理期 |
39+
| **架构** | 新增机制是否落在 §6.2 的三时刻表内 | 是。`requires/provides-interfaces` 在解析期,`[c-abi-absent]``link` 形状在链接期,`enosys` 在运行期。没有一项落在预处理期 |
4040
| **兼容性** | 未声明任何新键的包命令行是否逐字节不变 | 是。e2e 743 的 C 腿与 verify 的 D 段各验一次 |
41-
| | 两个新键对旧引擎的行为 | 不同,且已写入 docs/22 与其 zh 镜像:`[kernel-abi]` 被忽略,`[c-abi.absent]` 被拒。前者不需要抬 floor,后者需要 |
41+
| | 两个新键对旧引擎的行为 | 不同,且已写入 docs/22 与其 zh 镜像:`[kernel-abi]` 被忽略,`[c-abi-absent]` 被拒。前者不需要抬 floor,后者需要 |
4242
| **跨平台** | Windows / macOS / freestanding | 探针修复的直接受益者是 freestanding(此前在任何宿主上都空转)。Windows 上 clang 20.1.7 的三次前端崩溃是本轮实测发现,最终形态写下了理由 |
43-
| **一致性** | 同一个事实是否只有一处 | 提升了一处:openkal-musl 的 CI 手写 withheld 清单被 `[c-abi.absent]` 取代。降低了零处 |
43+
| **一致性** | 同一个事实是否只有一处 | 提升了一处:openkal-musl 的 CI 手写 withheld 清单被 `[c-abi-absent]` 取代。降低了零处 |
4444
| **无感升级** | 旧图是否照常构建 | 是。"一个什么都没陈述的提供者,不是一个什么都不提供的提供者"由 e2e 743 的 C 腿钉住 |
4545
| **测试覆盖** | 新增判据是否可证伪 | 逐条检查见 §3 |
46-
| **优雅简洁** | 新增概念数 | 一个(接口列举)。`[c-abi.absent]` 是既有 `[c-abi]` 的延伸,`assemble_argv` 是既有装配的提取 |
47-
| **用户体验** | 失败时读者能否知道改哪一边 | 接口拒绝同时点名缺的接口、要它的包、没提供它的实现;`[c-abi.absent]` 的诊断带上那一行的 `note` |
46+
| **优雅简洁** | 新增概念数 | 一个(接口列举)。`[c-abi-absent]` 是既有 `[c-abi]` 的延伸,`assemble_argv` 是既有装配的提取 |
47+
| **用户体验** | 失败时读者能否知道改哪一边 | 接口拒绝同时点名缺的接口、要它的包、没提供它的实现;`[c-abi-absent]` 的诊断带上那一行的 `note` |
4848

4949
---
5050

@@ -56,7 +56,7 @@ status: active
5656
| 探针量的是目标不是宿主 || 本机实测真实命令:`__riscv`、wchar 4、无 `__linux__` |
5757
| 接口不满足即拒绝 || e2e 743 的 B 腿,并断言**未编译任何东西** |
5858
| 提供者什么都没说时不拒绝 || e2e 743 的 C 腿 |
59-
| `[c-abi.absent]` 与产物一致 || openkal-musl CI 翻转一行并要求脚本拒绝 |
59+
| `[c-abi-absent]` 与产物一致 || openkal-musl CI 翻转一行并要求脚本拒绝 |
6060
| `provides-interfaces` 与产物一致 || openkal-linux / -windows CI 重新生成并 diff |
6161
| 接口清单不是"打印 SURFACE.txt 的每一组" || openkal CI 的半组腿:`openkal.fs` 只导出两个名字,必须不被列出 |
6262
| 沙箱 CHANGE 段确实区分两个版本 || 对 2026.9.17.1 跑出 fails=3,对 2026.9.20.1 跑出 fails=0 |
@@ -73,7 +73,7 @@ status: active
7373
| --- | --- | --- |
7474
| 1 | 接口清单取"图里第一个声明的包"而不是**被解析出的提供者** | 一个图可能有多个候选,读错的是另一个实现的清单——错误答案而不是缺失答案 |
7575
| 2 | 提供者用包名**子串**匹配 `impl` | `openkal` 命中 `openkal-linux@0.15.0` |
76-
| 3 | `[c-abi.absent]` 的诊断用符号**子串**匹配 | `undefined symbol: open``opendir` 的前缀;一个自信的错误解释比链接器自己的消息更坏 |
76+
| 3 | `[c-abi-absent]` 的诊断用符号**子串**匹配 | `undefined symbol: open``opendir` 的前缀;一个自信的错误解释比链接器自己的消息更坏 |
7777
| 4 | openkal CI 的接口步量的是 `impl-fd`,而它**一个接口都不整组提供** | 判据的对象选错了 |
7878
| 5 | openkal CI 的接口步读到上一步遗留的目标文件 | 上一步删了源文件没删产物 |
7979

0 commit comments

Comments
 (0)