/* file_api.c —— `Lib/shared/src/file.c` 那 8 个包装的**实参传递**判据。
 * file_api.c — criterion for **argument passing** in the 8 wrappers of `Lib/shared/src/file.c`.
 *
 * ## 为什么单独立一个
 * ## Why a dedicated probe
 *
 * 那批包装原先写的是 `asm("SYSCALL #110")` —— **一个寄存器都不装**：
 *   · C 调用点靠 R0–R3「兼容镜像」**碰巧**能跑，所以桌面上看着一直是对的；
 *   · `fsize`/`ftell`/`fseek`/`ftruncate` 四者都发 `#114` 却**连 op 号都没填**（R1 是上一次的残留）
 *     ⇒ **即使 C 也是错的**（`Examples/c/app/filedlg.c` 取文件大小读到垃圾）。
 * ⇒ 判据必须问「**传进去的值，宿主真收到了吗**」，而不是"函数有没有返回"。
 *
 * The wrappers used to be `asm("SYSCALL #110")` — **not loading a single register**. C call sites
 * worked by luck off the R0–R3 "compatibility mirror"; `fsize`/`ftell`/`fseek`/`ftruncate` all issued
 * `#114` **without even setting the op code** in R1. So the criterion must ask "did the host actually
 * receive the values", not "did the function return something".
 *
 * ## 判据
 * ## Criteria
 *
 * 建一个 5 字节的文件，然后逐项核对**数值**：写 5 ⇒ `fsize` 必须是 5（不是垃圾）、
 * `ftell` 必须是 5、三种 whence 各自定位到 5/3/0、`ftruncate(2)` 之后大小必须是 2。
 * 任何一项对不上就 FAIL 并以非 0 退出（`echo $?` 可直接当判据）。
 *
 * ⚠ 跑完在 **cwd** 留下一个 `_file_api_probe.txt`（沙箱根 = 工作区，相对路径）。
 *   Running it leaves a `_file_api_probe.txt` in the **cwd**.
 *
 * 跑法（桌面，秒级）：
 * How to run (desktop, seconds):
 *     dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll third_party/vml/Examples/c/test/file_api.c
 * 手机上：命令行页敲 `vml run examples/c/test/file_api.c`
 * On mobile: type `vml run examples/c/test/file_api.c` in the command line page.
 */

#include <file.h>

int fails = 0;

/* 逐项核对：对不上就记一笔，并把期望值一起打出来（"差多少"比"错了"有用）。 */
void ck(char* name, int got, int want) {
    if (got == want) {
        printf("OK   %s = %d\n", name, got);
    } else {
        printf("FAIL %s: got %d, want %d\n", name, got, want);
        fails = fails + 1;
    }
}

int main(void) {
    int h;
    int n;
    int sz;
    int pos;
    char buf[32];

    /* ① 只写建文件（mode 1 = 建 + 截断）—— 这条同时验 `fopen` 的**文件名指针**真的传进去了：
     *    文件名没传进去的话宿主读不到路径，句柄必然是负的。 */
    h = fopen("_file_api_probe.txt", 1);
    if (h < 0) {
        printf("FAIL fopen(write) = %d\n", h);
        return 1;
    }
    printf("OK   fopen(write) = %d\n", h);

    /* ② 写 5 字节 */
    n = fwrite(h, "hello", 5);
    ck("fwrite", n, 5);

    /* ③ 取大小 —— 本次修复的核心：原先 op 号没填，这里读到的是垃圾 */
    sz = fsize(h);
    ck("fsize", sz, 5);

    /* ④ 取当前位置（写完之后应停在 5） */
    pos = ftell(h);
    ck("ftell", pos, 5);

    /* ⑤ 从文件头定位（SEEK_SET = 0） */
    fseek(h, 0, 0);
    pos = ftell(h);
    ck("fseek SET", pos, 0);

    /* ⑥ 从末尾定位（SEEK_END = 2）—— 库内换算成绝对位置 */
    fseek(h, 0, 2);
    pos = ftell(h);
    ck("fseek END", pos, 5);

    /* ⑦ 从当前位置往前挪 2（SEEK_CUR = 1）⇒ 5 - 2 = 3 */
    fseek(h, -2, 1);
    pos = ftell(h);
    ck("fseek CUR", pos, 3);

    /* ⑧ 截断到 2 字节 */
    ftruncate(h, 2);
    sz = fsize(h);
    ck("ftruncate", sz, 2);

    fclose(h);

    /* ⑨ 只读打开 + 读回（mode 0 = 只读，文件已存在） */
    h = fopen("_file_api_probe.txt", 0);
    if (h < 0) {
        printf("FAIL fopen(read) = %d\n", h);
        return 1;
    }
    printf("OK   fopen(read) = %d\n", h);

    n = fread(h, buf, 2);
    if (n < 0) n = 0;
    buf[n] = 0;
    ck("fread", n, 2);
    printf("     text=%s\n", buf);

    fclose(h);

    printf("FILE_API %s (fails=%d)\n", fails == 0 ? "PASS" : "FAIL", fails);
    return fails;
}
