Skip to content

Windows compatibility fixes (spaces, stubs, includes) - #86

Open
kungfubeaner wants to merge 1 commit into
fmang:masterfrom
kungfubeaner:master
Open

Windows compatibility fixes (spaces, stubs, includes)#86
kungfubeaner wants to merge 1 commit into
fmang:masterfrom
kungfubeaner:master

Conversation

@kungfubeaner

@kungfubeaner kungfubeaner commented Aug 16, 2026

Copy link
Copy Markdown

This patch makes opustags compile and run correctly on Windows (MSYS2 UCRT64 / cmd.exe):

  • Fixed -e interactive editing win32 compatibility with filenames containing spaces by using double quotes in shell_escape on Windows.
  • Added missing <cstdint> includes in base64.cc, ogg.cc, opus.cc.
  • Centralised Windows stubs (strncasecmp, getdelim, mkstemps) into win32_compat.h.
  • Made sys/wait.h conditional to avoid Windows build error.
  • Added endianness macros (htole32, etc.) for Windows in opustags.h.

@kungfubeaner

kungfubeaner commented Aug 16, 2026

Copy link
Copy Markdown
Author

I compiled and tested it using this batch file:

@echo.
@rd /q /s build
@mkdir build && cd build
@cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXE_LINKER_FLAGS="-s -static -static-libgcc -static-libstdc++" -DIconv_LIBRARY="C:/msys64/ucrt64/lib/libiconv.a" -DOGG_LIBRARIES="C:/msys64/ucrt64/lib/libogg.a" ..
@make -j4
ntldd opustags.exe

I'm using MSYS2 UCRT64, it compiles into a static binary that does not require DLLs other than kernel32.dll and well the UCRT windows dlls which are already there in Windows 10 and Windows 11

@fmang fmang left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the pull request!

It is too big for me to review and merge it easily, so I’d be grateful if you split it. For example:

  1. the part with byte endianness and cstdint,
  2. the file manipulation operations,
  3. the compatibility functions.

Comment thread src/opustags.h
#endif

#if defined(_WIN32) || defined(__MINGW32__)
#include <cstdint>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than include cstdint in the .h and in every .cc, including it in opustags.h only is enough since all the .cc include it.

Comment thread src/cli.cc

// Applying the new tags.
tags_file = fopen(tags_path.c_str(), "re");
tags_file = fopen(tags_path.c_str(), "rb");

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the e flag?

Comment thread src/opustags.h
inline uint32_t le32toh(uint32_t x) { return x; }
inline uint32_t htobe32(uint32_t x) {
return ((x & 0xffu) << 24) | ((x & 0xff00u) << 8) |
((x & 0xff0000u) >> 8) | ((x >> 24) & 0xffu);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the & 0xffu necessary?

Also, doesn’t Windows already provide such a function, like htonl?

Comment thread src/system.cc
#include <unistd.h>
#include <cstdint>

#ifndef _WIN32

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it curious to mix #ifndef and #if defined. Why not use an #else instead?

Comment thread src/system.cc
{
#ifdef _WIN32
// Windows cmd.exe: double quotes protect spaces.
return "\"" + std::string(word) + "\"";

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If word contains a double quote, that’s an open door to serious shell injection vulnerabilities.

Comment thread src/cli.cc
#include <algorithm>
#include <cstdint>

#if defined(_WIN32) || defined(__MINGW32__)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why check both _WIN32 and __MINGW32__. Isn’t _WIN32 enough?

Comment thread src/system.cc
return;
file.reset();
copy_permissions(final_name.c_str(), temporary_name.c_str());
#if defined(_WIN32) || defined(__MINGW32__)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could put the call to copy_permissions in an #else here instead of defining a blank function.

Comment thread src/system.cc
std::string command = std::string(editor) + " -- " + shell_escape(path);
#endif

int status = system(command.c_str());

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the only line in common between Windows and Unix, you might as well make on big #if instead of two halves.

Comment thread src/win32_compat.h
#define strncasecmp _strnicmp

#ifndef getdelim
static inline ssize_t getdelim(char** lineptr, size_t* n, int delim, FILE* stream) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is huge for an inline function. You could move the definition into system.cc and add the declaration into opustags.h instead so that it is built and linked like a regular function.

By the way, where does the implementation come from?

@kungfubeaner kungfubeaner Aug 17, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote the getdelim (and mkstemps) implementations myself to get the Windows build working. I looked at a few public versions for reference while writing it, but this is my own code. Happy to move both functions out of the header into system.cc (and just declare them in the header or in opustags.h) if you prefer that style. I can also clean them up further if you’d like.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Since C++ is loaded with footguns so I’ll make sure to review it attentively, unless we find a battle-tested alternative.

Maybe we could replace getdelim with std::getline, provided the code does not rely too much on stdio. Regarding mkstemps, Windows seems to have _mktemp that could do some of the work.

@kungfubeaner kungfubeaner Aug 18, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I will look into _mktemp, Windows seems very messy. I'll look into all this more thoroughly on Saturday including using getline instead of getdelim. My PR was pretty much out of excitement that I actually got it to compile and work on Windows. I've been wanting / needing opustags for Windows for a while.

Comment thread src/win32_compat.h

#define strncasecmp _strnicmp

#ifndef getdelim

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for? Is it possible that getdelim is sometimes defined as a macro, sometimes not, but never defined as a function?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is so it uses the windows equivalent of strncasecmp, I'll work on all these changes and make it suitable. I have a busy week coming up and start work in 2 hours, I'l try to clean all this up and explain on Saturday.

Sorry for the lack of explanation on these changes and having to wait until Saturday.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I misunderstood your question. I added the #ifndef getdelim because I wanted to prevent a duplicate definition if the platform already supplied getdelim(). I realize now that this only checks for a preprocessor macro, not whether the function is already provided.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. If the _WIN32 macro check is not enough, we should rely on CMake because that’s precisely what it’s for. See CheckFunctionExists.

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