Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@curiousdannii
Copy link
Contributor

This seems to be working with @EmptySpaceRun's game that uses the Z-Machine's @save/@restore no-prompt data file feature.

My C++ is very rudimentary, but does it look okay to you?

@curiousdannii curiousdannii changed the title Add ZTERP_GLK_NO_STDIO version of zterp_os_aux_file Bocfel: Add ZTERP_GLK_NO_STDIO version of zterp_os_aux_file Oct 18, 2025
@cspiegel
Copy link
Contributor

A couple comments:

At least nominally the idea of "no stdio mode" is orthogonal to the OS. There's probably no reason to, but it's possible to build on Unix with no stdio, for example, assuming the Glk target implements glkunix_fileref_create_by_name_uncleaned. With that in mind, this should be gated by a new OS type (e.g. ZTERP_OS_PARCHMENT, or whatever you'd like). So you wind up with:

// ╔══════════════════════════════════════════════════════════════════════════════╗
// ║ Parchment functions                                                          ║
// ╚══════════════════════════════════════════════════════════════════════════════╝
#elif defined(ZTERP_OS_PARCHMENT)
#if defined(ZTERP_GLK_NO_STDIO) && defined(GLKUNIX_FILEREF_GET_FILENAME)
// code goes here
#endif

Then just add -DZTERP_OS_PARCHMENT while building.

Since you're using glkunix_fileref_get_filename this also checks GLKUNIX_FILEREF_GET_FILENAME. It's not really necessary since you "know" Parchment will provide it, but I'm trying to be forward-looking here, just in case.

This dovetails into the next point: this doesn't do any sort of filename sanitizing. I assume that under Parchment that's irrelevant because the browser provides sandboxing of some sort? If so, then having the OS type of Parchment is a good way to signal that: similar to the previous comment, if somebody was building with Glk on a non-supported OS (or no OS provided at all), but did enable no stdio mode, then all of a sudden this code is active, allowing games to escape their containing directory. But if you just mandate that if you're building for the "operating system" of Parchment, you're guaranteed to be sandboxed, or guaranteed that the concept of escaping a containing directory is nonsensical, we're fine.

@curiousdannii
Copy link
Contributor Author

curiousdannii commented Oct 18, 2025

At least nominally the idea of "no stdio mode" is orthogonal to the OS. There's probably no reason to, but it's possible to build on Unix with no stdio, for example, assuming the Glk target implements glkunix_fileref_create_by_name_uncleaned. With that in mind, this should be gated by a new OS type (e.g. ZTERP_OS_PARCHMENT, or whatever you'd like).

It's already required to be defined alongside ZTERP_GLK_UNIX:

#ifdef ZTERP_GLK_NO_STDIO
#ifndef ZTERP_GLK_UNIX
#error ZTERP_GLK_NO_STDIO requires a Unix Glk
#endif

Do you think it would be better to check both are defined here?

Since you're using glkunix_fileref_get_filename this also checks GLKUNIX_FILEREF_GET_FILENAME. It's not really necessary since you "know" Parchment will provide it, but I'm trying to be forward-looking here, just in case.

Oh yes, I'll add a check for that.

But I noticed that io.cpp also requires that if you're using ZTERP_GLK_NO_STDIO then GLKUNIX_FILEREF_CREATE_UNCLEANED must be defined:

#ifndef GLKUNIX_FILEREF_CREATE_UNCLEANED
#error ZTERP_GLK_NO_STDIO requires the extension glkunix_fileref_create_by_name_uncleaned
// Prototype so that usage of this function doesn’t cause a compile error.
frefid_t glkunix_fileref_create_by_name_uncleaned(glui32 usage, const char *name, glui32 rock);
#endif

Should we put all of these checks together in one place? I don't know how a C++ project would typically be structured to do that. Does it matter which file the checks happen in - are they compiled in alphabetical order?

this doesn't do any sort of filename sanitizing.

glk_fileref_create_by_name does sanitise. (Strictly speaking the spec only has a recommendation, but I think all recent Glk libraries follow it. If you're using a non-sanitising library then that's a risk with ordinary Glulx storyfiles too.) It also takes care of choosing the folder (which is library dependent - some use the same location as the storyfile, others put them in a separate data folder.)

@cspiegel
Copy link
Contributor

Man, this really is kind of getting to be a mess of macros, isn't it?

I didn't think about glk_fileref_create_by_name already sanitizing. I'm with you on that: if the library has a broken glk_fileref_create_by_name then it's not our fault.

For the macros, it's just not going to be pretty (par for the course with C/C++ macros). In C++, the macro checks really do need to be right around where they're being used. Build order isn't guaranteed at all, since you can tell make to run jobs in parallel.

So forget I said anything about sanitizing, but we'll still want the excessive macro checks. However... this got me thinking. Your code isn't really tied to no-stdio mode at all. It's basically generic Glk code to provide an auxiliary filename. I didn't realize it earlier, but it's clear now once you mentioned that sanitizing comes for free.

I think, then, this should be in the generic function definition, if the right conditions (Glk with glkunix_fileref_get_filename) are met. I suggest the following:

#ifndef have_zterp_os_aux_file
#ifdef ZTERP_GLK_UNIX
extern "C" {
#include <glkstart.h>
}
#endif

std::unique_ptr<std::string> zterp_os_aux_file(const std::string &filename)
{
#if defined(ZTERP_GLK_UNIX) && defined(GLKUNIX_FILEREF_GET_FILENAME)
    frefid_t fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode, const_cast<char *>(filename.c_str()), 0);
    if (fref != nullptr) {
        auto result = std::make_unique<std::string>(glkunix_fileref_get_filename(fref));
        glk_fileref_destroy(fref);
        return result;
    }
#endif
    return nullptr;
}
#endif

This gets around the need for a new operating system type and also doesn't have to even worry about no-stdio mode outside of io.cpp. And it provides auxiliary files to all Glk implementations, even those not covered under a specific OS.

@curiousdannii
Copy link
Contributor Author

Your code isn't really tied to no-stdio mode at all. It's basically generic Glk code to provide an auxiliary filename.

That is the purpose of the no-stdio mode as a whole, to use the glkunix functions rather than stdio functions ;) It's just not necessary anywhere other than Emglken, and sometimes it's a bit clumsy. Glulxe, Git, Hugo and Scare did that already, they never use the stdio filesystem functions, which made it very easy to use with Emglken's very weird FS-less environment. Bocfel only needed tiny changes, and TADS needed.. a lot haha. I don't think Bocfel should be aiming to switch to Glk only (which of course it can't do as it has non-Glk IO modes!) but it is nice when we can get it working in Emglken with minimal changes.

Great idea to (if supported) make this the default implementation! I'll update the PR to do that.

@cspiegel
Copy link
Contributor

OK, looks great! Thanks!

@cspiegel cspiegel merged commit 52e48f1 into garglk:master Oct 19, 2025
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants