Enabling globbing in a console Win32 application

written on Thu 16 October 2014

"Globbing" can be also described as an act of expanding the filename wildcards supplied in the command-line of a console application.

Let's suppose we have a small command-line application that performs some operation on files supplied as arguments in the command-line.

#include <iostream>
#include <string>

int main(int argc, char **argv) {
    for(int i = 1; i < argc; i++) {
        std::cout << argv[i] << "\n";
    }

    return 0;
}

When running this application on MacOS X, or Linux, or any other platform that uses an power-user-grade shell, like bash, tcsh or zsh, invocation of this program with a wildcard in the argument would result in something like this:

$ touch file{1,2,3}.bin

$ g++ test.cpp -o test

$ ./test *.bin
file1.bin
file2.bin
file3.bin

This behavior is a result of an implicit globbing that occurs before the actual invocation of our program, and is performed by the shell automatically. For example, you can turn off globbing, at least in zsh:

$ noglob ./test *.bin
*.bin

This will probably be quite useless in most cases, but even if you actually expect to have this behavior in your application, you can also expand supplied wildcards later by using the glob(3) function, which is available on MacOS X, Linux, BSDs and other systems with APIs compatible more or less with 4.4BSD.

On Windows however, the "shell" in the form of cmd.exe doesn't seem to support such feature. Every console application is expected to perform globbing by itself, but which API to use? There's one option to use _findfirst, or FindFirst, which should work, and another method that uses PathMatchSpec WinAPI function, but using them is not portable and is actually an extra work that needs to be done (not cool, right?).

It turns out that the solution is pretty simple, although definitely not intuitive. You can link setargv.obj file (available in your standard Visual Studio distribution) to your project, and the main() function will automatically contain already globbed filenames, so no wildcard preprocessing will be needed.

If you're using CMake, you can try something like this:

cmake_minimum_required(VERSION 3.0.0)
set(SRC test.cpp)
add_executable(testapp ${SRC})

# Linking setargv.obj to automatically enable globbing
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} setargv.obj")

If you're not using CMake, have you considered trying it?

If you need unicode support, try linking wsetargv.obj file instead. After linking the file, you can test it:

C:\dir>testapp.exe *.bin
file1.bin
file2.bin
file3.bin

It works, and we didn't even use MinGW!

This entry was tagged on #c++ and #windows