Add cross-platform Windows and Linux CI builds
Introduce a reproducible CMake build and GitHub Actions matrix for Ubuntu Linux x64 and Windows x64. Each platform installs libusb, builds the usbboot executable, and publishes a platform-specific artifact containing the executable, required boot binaries, and documentation. The Windows artifact also includes libusb-1.0.dll. Add a small Windows getopt_long compatibility layer because the Microsoft CRT does not provide the POSIX GNU option parser used by usbboot. Add Windows sleep support and make the source explicitly include the fixed-width integer and time headers needed by the cross-platform build. Replace C99 variable-length arrays with checked heap allocations so the source compiles with MSVC, which does not support VLAs. Cast libusb request-type flags to avoid MSVC enum warnings, and use a conforming cleanup(void) callback signature for atexit(). Correct the existing cleanup NULL check so an open libusb handle is actually closed. Document local CMake builds, CI artifacts, the Windows libusb DLL, and the requirement to bind the boot-mode device to WinUSB before use. Credit: OpenAI Codex (GPT-5), collaborating with dark98 on the Windows port and CI implementation.
This commit is contained in:
+122
@@ -0,0 +1,122 @@
|
||||
#include "getopt_compat.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
char *optarg;
|
||||
int optind = 1;
|
||||
int opterr = 1;
|
||||
int optopt;
|
||||
|
||||
static const char *short_options;
|
||||
|
||||
static int option_requires_argument(const char *optstring, int option)
|
||||
{
|
||||
const char *match = strchr(optstring, option);
|
||||
return match != NULL && match[1] == ':';
|
||||
}
|
||||
|
||||
static int missing_argument(int option)
|
||||
{
|
||||
optopt = option;
|
||||
optarg = NULL;
|
||||
return '?';
|
||||
}
|
||||
|
||||
int getopt_long(int argc, char * const argv[], const char *optstring,
|
||||
const struct option *longopts, int *longindex)
|
||||
{
|
||||
const char *arg;
|
||||
|
||||
optarg = NULL;
|
||||
if (optind >= argc)
|
||||
return -1;
|
||||
|
||||
arg = argv[optind];
|
||||
if (arg[0] != '-' || arg[1] == '\0')
|
||||
return -1;
|
||||
|
||||
if (arg[1] == '-') {
|
||||
const char *name = arg + 2;
|
||||
const char *equals = strchr(name, '=');
|
||||
size_t name_length = equals != NULL
|
||||
? (size_t)(equals - name)
|
||||
: strlen(name);
|
||||
size_t i;
|
||||
|
||||
if (name_length == 0) {
|
||||
optind++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; longopts[i].name != NULL; i++) {
|
||||
if (strlen(longopts[i].name) != name_length ||
|
||||
strncmp(longopts[i].name, name, name_length) != 0)
|
||||
continue;
|
||||
|
||||
if (longindex != NULL)
|
||||
*longindex = (int)i;
|
||||
|
||||
if (longopts[i].has_arg == required_argument) {
|
||||
if (equals != NULL)
|
||||
optarg = (char *)(equals + 1);
|
||||
else if (optind + 1 < argc)
|
||||
optarg = argv[++optind];
|
||||
else
|
||||
return missing_argument(longopts[i].val);
|
||||
} else if (equals != NULL) {
|
||||
return missing_argument(longopts[i].val);
|
||||
}
|
||||
|
||||
optind++;
|
||||
if (longopts[i].flag != NULL) {
|
||||
*longopts[i].flag = longopts[i].val;
|
||||
return 0;
|
||||
}
|
||||
return longopts[i].val;
|
||||
}
|
||||
|
||||
optind++;
|
||||
optopt = 0;
|
||||
return '?';
|
||||
}
|
||||
|
||||
/* Parse one short option, including clusters such as -vbh. */
|
||||
if (short_options == NULL || *short_options == '\0')
|
||||
short_options = arg + 1;
|
||||
|
||||
{
|
||||
int option = (unsigned char)*short_options++;
|
||||
const char *match = strchr(optstring, option);
|
||||
|
||||
if (match == NULL || option == ':') {
|
||||
optopt = option;
|
||||
if (*short_options == '\0') {
|
||||
short_options = NULL;
|
||||
optind++;
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
if (option_requires_argument(optstring, option)) {
|
||||
if (*short_options != '\0') {
|
||||
optarg = (char *)short_options;
|
||||
short_options = NULL;
|
||||
optind++;
|
||||
} else if (optind + 1 < argc) {
|
||||
optarg = argv[++optind];
|
||||
short_options = NULL;
|
||||
optind++;
|
||||
} else {
|
||||
short_options = NULL;
|
||||
optind++;
|
||||
return missing_argument(option);
|
||||
}
|
||||
} else if (*short_options == '\0') {
|
||||
short_options = NULL;
|
||||
optind++;
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user