← 都来码 / Dolaima← 例程索引c/test/file_api.c原文

c/test/file_api.c · 4312 字节 · 下载原文

/* 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;
}