Skip to content

Instantly share code, notes, and snippets.

@Artoria2e5
Last active October 5, 2020 01:06
Show Gist options
  • Save Artoria2e5/aa706d9b5801739c30c7ef15cc3a5927 to your computer and use it in GitHub Desktop.
Save Artoria2e5/aa706d9b5801739c30c7ef15cc3a5927 to your computer and use it in GitHub Desktop.
getopt(3) in the Solaris way, as a wrapper around getopt_long(3)
/**
* sun_getopt - Solaris-style getopt(3), as a wrapper around getopt_long(3).
*
* Written by Mingye Wang (Artoria2e5). Dedicated to the public domain under
* Creative Commons Zero.
*
* Untested. Like not even fed to a compiler once.
*/
#include "sun_getopt.h"
#include <getopt.h>
#include <stddef.h>
#include <stdlib.h>
struct sun_longopts_s {
const char *sun;
char *posix;
struct option *opts;
};
#if __STDC_VERSION__ >= 201112L
static _Thread_local sun_longopts_t store = {NULL, NULL, NULL};
#else
static sun_longopts_t store = {NULL, NULL, NULL};
#endif
static int sun_getopt_compile(const char *__restrict from, sun_longopts_t *__restrict to) {
size_t len = strlen(from);
size_t opt_count = 0;
to->sun = from;
to->posix = malloc(len + 1);
if (!to->posix) return 0;
size_t opt_size = 16;
to->opts = malloc(opt_size * sizeof(struct option));
if (!to->opts) return 0;
int optchar = '\0';
char *to_str = to->posix;
const char* paren_start = NULL;
size_t colons = 0;
for (size_t i = 0; i < len; i++) {
if (!paren_start) {
if (from[i] == '(') {
in_paren = 1;
paren_start = &from[i];
continue;
}
if (from[i] != ':') {
colons = 0;
optchar = from[i];
} else {
colons++;
}
// Maybe we should filter out unprintable optchars? So they can be used as indices but not short args.
*to_str++ = from[i];
} else if (from[i] == ')') {
if (++opt_count >= opt_size - 1)
if (!(to->opts = realloc(to->opts,
(opt_size *= 2) * sizeof(struct option))))
return 0;
to->opts[opt_count] = {
strndup(paren_start + 1, &from[i] - paren_startn - 1),
colons > 1 ? optional_argument : colon == 1 ? required_argument : no_argument,
NULL,
optchar
};
paren_start = NULL;
colons = 0;
optchar = '\0';
}
}
*to_str = '\0';
to->opts[++opt_count] = {NULL, 0, NULL, 0};
return !paren_start;
}
int sun_getopt(int argc, char *const argv[], const char *optstring) {
if (!store.posix || strcmp(store.sun, optstring)) {
int status = sun_getopt_compile(optstring, &store);
if (!status)
return EOF; // FIXME
}
return getopt_long(argc, argv, store.posix, store.opts, NULL);
}
/**
* sun_getopt - Solaris-style getopt(3), as a wrapper around getopt_long(3).
*
* Written by Mingye Wang (Artoria2e5). Dedicated to the public domain under
* Creative Commons Zero.
*/
#ifndef SUN_GETOPT_H
#define SUN_GETOPT_H
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
extern int optreset;
#endif
typedef struct sun_longopts_s sun_longopts_t;
int sun_getopt(int argc, char *const argv[], const char *optstring);
void sun_getopt_finish()
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment