CX+AI

Files in, files out

Read a file, write a file, and what changes in a browser tab

Files in, files out

Read a whole file into a string, write a string to a file. Everything else is a variation on those two, and both are one call.

One page. Everything here was run.


Write one, read it back

path.s = "notes.txt";

fwrite(path, "one" + chr(10) + "two" + chr(10) + "three" + chr(10));

printf("exists=%d size=%d\n", fileExists(path), fileSize(path));

whole.s = fread(path);
printf("read %d chars\n", strlen(whole));
exists=1 size=14
read 14 chars

fwrite replaces; fappend adds to the end. Neither needs an open, a handle or a close, because the whole-file case is the one almost every program actually wants and a handle is ceremony you would only be closing again.


Walk it line by line

There is no line reader, because a file is a string and a string splits.

whole.s = "one" + chr(10) + "two" + chr(10) + "three" + chr(10);

list rows.s;
n.i = split(whole, chr(10), rows);
i.i = 0;

foreach rows {
    row.s = listGet(rows, i);
    if (row != "") { printf("  %d: %s\n", i + 1, row); }
    i = i + 1;
}
  1: one
  2: two
  3: three

A file that ends in a newline splits into one more piece than it has lines, and the last one is empty. That is arithmetic rather than a quirk — three separators make four pieces — and the if above is how every program that reads a text file deals with it.


Add to one, then remove it

path.s = "notes.txt";

fappend(path, "four" + chr(10));
printf("after append: %d chars\n", strlen(fread(path)));

fdelete(path);
printf("exists now=%d\n", fileExists(path));
after append: 19 chars
exists now=0

Reading a file that is not there

fread on a missing path gives you an empty string, and fileExists is how you tell that from a file that is genuinely empty. Ask before you read when the difference matters:

path.s = "definitely_not_here.txt";

if (fileExists(path)) {
    printf("%s\n", fread(path));
} else {
    printf("no such file\n");
}
no such file

A file of JSON is two calls

fwrite("cfg.json", "{ \"port\": 8080, \"host\": \"localhost\" }");

json cfg = parseDoc(fread("cfg.json"));

if (cfg->valid) {
    port.i = cfg["port"];
    host.s = cfg["host"];
    printf("%s:%d\n", host, port);
}

fdelete("cfg.json");
localhost:8080

Read it, parse it, and the values come out as the types you declared. See Read some JSON and use it for the rest.


In a browser tab

A CX+AI program compiled to WebAssembly still has all of this, and the files are real but private: they live in the tab's own filesystem, they persist while the page does, and nothing reaches the machine the browser is running on.

That is the honest position rather than a limitation to apologise for — a page that could write to your disk would be a page you should not open. If a program needs to hand a file to the person looking at it, that is a download, which is the page's job rather than the program's.


Where to go next