#if defined(SOKOL_IMPL) && !defined(SOKOL_ARGS_IMPL)
#define SOKOL_ARGS_IMPL
#endif
#ifndef SOKOL_ARGS_INCLUDED
#define SOKOL_ARGS_INCLUDED (1)
#include <stdint.h>
#include <stdbool.h>
#if defined(SOKOL_API_DECL) && !defined(SOKOL_ARGS_API_DECL)
#define SOKOL_ARGS_API_DECL SOKOL_API_DECL
#endif
#ifndef SOKOL_ARGS_API_DECL
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_ARGS_IMPL)
#define SOKOL_ARGS_API_DECL __declspec(dllexport)
#elif defined(_WIN32) && defined(SOKOL_DLL)
#define SOKOL_ARGS_API_DECL __declspec(dllimport)
#else
#define SOKOL_ARGS_API_DECL extern
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct sargs_desc {
int argc;
char** argv;
int max_args;
int buf_size;
} sargs_desc;
SOKOL_ARGS_API_DECL void sargs_setup(const sargs_desc* desc);
SOKOL_ARGS_API_DECL void sargs_shutdown(void);
SOKOL_ARGS_API_DECL bool sargs_isvalid(void);
SOKOL_ARGS_API_DECL bool sargs_exists(const char* key);
SOKOL_ARGS_API_DECL const char* sargs_value(const char* key);
SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def);
SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val);
SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key);
SOKOL_ARGS_API_DECL int sargs_find(const char* key);
SOKOL_ARGS_API_DECL int sargs_num_args(void);
SOKOL_ARGS_API_DECL const char* sargs_key_at(int index);
SOKOL_ARGS_API_DECL const char* sargs_value_at(int index);
#ifdef __cplusplus
}
inline void sargs_setup(const sargs_desc& desc) { return sargs_setup(&desc); }
#endif
#endif
#ifdef SOKOL_ARGS_IMPL
#define SOKOL_ARGS_IMPL_INCLUDED (1)
#include <string.h>
#if defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#endif
#ifndef SOKOL_API_IMPL
#define SOKOL_API_IMPL
#endif
#ifndef SOKOL_DEBUG
#ifndef NDEBUG
#define SOKOL_DEBUG (1)
#endif
#endif
#ifndef SOKOL_ASSERT
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#if !defined(SOKOL_CALLOC) && !defined(SOKOL_FREE)
#include <stdlib.h>
#endif
#if !defined(SOKOL_CALLOC)
#define SOKOL_CALLOC(n,s) calloc(n,s)
#endif
#if !defined(SOKOL_FREE)
#define SOKOL_FREE(p) free(p)
#endif
#ifndef SOKOL_LOG
#ifdef SOKOL_DEBUG
#include <stdio.h>
#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); }
#else
#define SOKOL_LOG(s)
#endif
#endif
#ifndef _SOKOL_PRIVATE
#if defined(__GNUC__) || defined(__clang__)
#define _SOKOL_PRIVATE __attribute__((unused)) static
#else
#define _SOKOL_PRIVATE static
#endif
#endif
#define _sargs_def(val, def) (((val) == 0) ? (def) : (val))
#define _SARGS_MAX_ARGS_DEF (16)
#define _SARGS_BUF_SIZE_DEF (16*1024)
#define _SARGS_EXPECT_KEY (1<<0)
#define _SARGS_EXPECT_SEP (1<<1)
#define _SARGS_EXPECT_VAL (1<<2)
#define _SARGS_PARSING_KEY (1<<3)
#define _SARGS_PARSING_VAL (1<<4)
#define _SARGS_ERROR (1<<5)
typedef struct {
int key;
int val;
} _sargs_kvp_t;
typedef struct {
int max_args;
int num_args;
_sargs_kvp_t* args;
int buf_size;
int buf_pos;
char* buf;
bool valid;
uint32_t parse_state;
char quote;
bool in_escape;
} _sargs_state_t;
static _sargs_state_t _sargs;
_SOKOL_PRIVATE void _sargs_putc(char c) {
if ((_sargs.buf_pos+2) < _sargs.buf_size) {
_sargs.buf[_sargs.buf_pos++] = c;
}
}
_SOKOL_PRIVATE const char* _sargs_str(int index) {
SOKOL_ASSERT((index >= 0) && (index < _sargs.buf_size));
return &_sargs.buf[index];
}
_SOKOL_PRIVATE void _sargs_expect_key(void) {
_sargs.parse_state = _SARGS_EXPECT_KEY;
}
_SOKOL_PRIVATE bool _sargs_key_expected(void) {
return 0 != (_sargs.parse_state & _SARGS_EXPECT_KEY);
}
_SOKOL_PRIVATE void _sargs_expect_val(void) {
_sargs.parse_state = _SARGS_EXPECT_VAL;
}
_SOKOL_PRIVATE bool _sargs_val_expected(void) {
return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL);
}
_SOKOL_PRIVATE void _sargs_expect_sep(void) {
_sargs.parse_state = _SARGS_EXPECT_SEP;
}
_SOKOL_PRIVATE bool _sargs_any_expected(void) {
return 0 != (_sargs.parse_state & (_SARGS_EXPECT_KEY | _SARGS_EXPECT_VAL | _SARGS_EXPECT_SEP));
}
_SOKOL_PRIVATE bool _sargs_is_separator(char c) {
return c == '=';
}
_SOKOL_PRIVATE bool _sargs_is_quote(char c) {
if (0 == _sargs.quote) {
return (c == '\'') || (c == '"');
}
else {
return c == _sargs.quote;
}
}
_SOKOL_PRIVATE void _sargs_begin_quote(char c) {
_sargs.quote = c;
}
_SOKOL_PRIVATE void _sargs_end_quote(void) {
_sargs.quote = 0;
}
_SOKOL_PRIVATE bool _sargs_in_quotes(void) {
return 0 != _sargs.quote;
}
_SOKOL_PRIVATE bool _sargs_is_whitespace(char c) {
return !_sargs_in_quotes() && ((c == ' ') || (c == '\t'));
}
_SOKOL_PRIVATE void _sargs_start_key(void) {
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
_sargs.parse_state = _SARGS_PARSING_KEY;
_sargs.args[_sargs.num_args].key = _sargs.buf_pos;
}
_SOKOL_PRIVATE void _sargs_end_key(void) {
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
_sargs_putc(0);
_sargs.parse_state = 0;
}
_SOKOL_PRIVATE bool _sargs_parsing_key(void) {
return 0 != (_sargs.parse_state & _SARGS_PARSING_KEY);
}
_SOKOL_PRIVATE void _sargs_start_val(void) {
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
_sargs.parse_state = _SARGS_PARSING_VAL;
_sargs.args[_sargs.num_args].val = _sargs.buf_pos;
}
_SOKOL_PRIVATE void _sargs_end_val(void) {
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
_sargs_putc(0);
_sargs.num_args++;
_sargs.parse_state = 0;
}
_SOKOL_PRIVATE bool _sargs_is_escape(char c) {
return '\\' == c;
}
_SOKOL_PRIVATE void _sargs_start_escape(void) {
_sargs.in_escape = true;
}
_SOKOL_PRIVATE bool _sargs_in_escape(void) {
return _sargs.in_escape;
}
_SOKOL_PRIVATE char _sargs_escape(char c) {
switch (c) {
case 'n': return '\n';
case 't': return '\t';
case 'r': return '\r';
case '\\': return '\\';
default: return c;
}
}
_SOKOL_PRIVATE void _sargs_end_escape(void) {
_sargs.in_escape = false;
}
_SOKOL_PRIVATE bool _sargs_parsing_val(void) {
return 0 != (_sargs.parse_state & _SARGS_PARSING_VAL);
}
_SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
char c;
while (0 != (c = *src++)) {
if (_sargs_in_escape()) {
c = _sargs_escape(c);
_sargs_end_escape();
}
else if (_sargs_is_escape(c)) {
_sargs_start_escape();
continue;
}
if (_sargs_any_expected()) {
if (!_sargs_is_whitespace(c)) {
if (_sargs_key_expected()) {
_sargs_start_key();
}
else if (_sargs_val_expected()) {
if (_sargs_is_quote(c)) {
_sargs_begin_quote(c);
continue;
}
_sargs_start_val();
}
else {
if (_sargs_is_separator(c)) {
_sargs_expect_val();
continue;
}
}
}
else {
continue;
}
}
else if (_sargs_parsing_key()) {
if (_sargs_is_whitespace(c) || _sargs_is_separator(c)) {
_sargs_end_key();
if (_sargs_is_separator(c)) {
_sargs_expect_val();
}
else {
_sargs_expect_sep();
}
continue;
}
}
else if (_sargs_parsing_val()) {
if (_sargs_in_quotes()) {
if (_sargs_is_quote(c)) {
_sargs_end_quote();
_sargs_end_val();
_sargs_expect_key();
continue;
}
}
else if (_sargs_is_whitespace(c)) {
_sargs_end_val();
_sargs_expect_key();
continue;
}
}
_sargs_putc(c);
}
if (_sargs_parsing_key()) {
_sargs_end_key();
_sargs_expect_sep();
}
else if (_sargs_parsing_val() && !_sargs_in_quotes()) {
_sargs_end_val();
_sargs_expect_key();
}
return true;
}
_SOKOL_PRIVATE bool _sargs_parse_cargs(int argc, const char** argv) {
_sargs_expect_key();
bool retval = true;
for (int i = 1; i < argc; i++) {
retval &= _sargs_parse_carg(argv[i]);
}
_sargs.parse_state = 0;
return retval;
}
#if defined(__EMSCRIPTEN__)
#ifdef __cplusplus
extern "C" {
#endif
EMSCRIPTEN_KEEPALIVE void _sargs_add_kvp(const char* key, const char* val) {
SOKOL_ASSERT(_sargs.valid && key && val);
if (_sargs.num_args >= _sargs.max_args) {
return;
}
char c;
_sargs.args[_sargs.num_args].key = _sargs.buf_pos;
const char* ptr = key;
while (0 != (c = *ptr++)) {
_sargs_putc(c);
}
_sargs_putc(0);
_sargs.args[_sargs.num_args].val = _sargs.buf_pos;
ptr = val;
while (0 != (c = *ptr++)) {
_sargs_putc(c);
}
_sargs_putc(0);
_sargs.num_args++;
}
#ifdef __cplusplus
}
#endif
EM_JS(void, sargs_js_parse_url, (void), {
var params = new URLSearchParams(window.location.search).entries();
for (var p = params.next(); !p.done; p = params.next()) {
var key = p.value[0];
var val = p.value[1];
var res = ccall('_sargs_add_kvp', 'void', ['string','string'], [key,val]);
}
});
#endif
SOKOL_API_IMPL void sargs_setup(const sargs_desc* desc) {
SOKOL_ASSERT(desc);
memset(&_sargs, 0, sizeof(_sargs));
_sargs.max_args = _sargs_def(desc->max_args, _SARGS_MAX_ARGS_DEF);
_sargs.buf_size = _sargs_def(desc->buf_size, _SARGS_BUF_SIZE_DEF);
SOKOL_ASSERT(_sargs.buf_size > 8);
_sargs.args = (_sargs_kvp_t*) SOKOL_CALLOC((size_t)_sargs.max_args, sizeof(_sargs_kvp_t));
_sargs.buf = (char*) SOKOL_CALLOC((size_t)_sargs.buf_size, sizeof(char));
_sargs.buf_pos = 1;
_sargs.valid = true;
_sargs_parse_cargs(desc->argc, (const char**) desc->argv);
#if defined(__EMSCRIPTEN__)
sargs_js_parse_url();
#endif
}
SOKOL_API_IMPL void sargs_shutdown(void) {
SOKOL_ASSERT(_sargs.valid);
if (_sargs.args) {
SOKOL_FREE(_sargs.args);
_sargs.args = 0;
}
if (_sargs.buf) {
SOKOL_FREE(_sargs.buf);
_sargs.buf = 0;
}
_sargs.valid = false;
}
SOKOL_API_IMPL bool sargs_isvalid(void) {
return _sargs.valid;
}
SOKOL_API_IMPL int sargs_find(const char* key) {
SOKOL_ASSERT(_sargs.valid && key);
for (int i = 0; i < _sargs.num_args; i++) {
if (0 == strcmp(_sargs_str(_sargs.args[i].key), key)) {
return i;
}
}
return -1;
}
SOKOL_API_IMPL int sargs_num_args(void) {
SOKOL_ASSERT(_sargs.valid);
return _sargs.num_args;
}
SOKOL_API_IMPL const char* sargs_key_at(int index) {
SOKOL_ASSERT(_sargs.valid);
if ((index >= 0) && (index < _sargs.num_args)) {
return _sargs_str(_sargs.args[index].key);
}
else {
return _sargs_str(0);
}
}
SOKOL_API_IMPL const char* sargs_value_at(int index) {
SOKOL_ASSERT(_sargs.valid);
if ((index >= 0) && (index < _sargs.num_args)) {
return _sargs_str(_sargs.args[index].val);
}
else {
return _sargs_str(0);
}
}
SOKOL_API_IMPL bool sargs_exists(const char* key) {
SOKOL_ASSERT(_sargs.valid && key);
return -1 != sargs_find(key);
}
SOKOL_API_IMPL const char* sargs_value(const char* key) {
SOKOL_ASSERT(_sargs.valid && key);
return sargs_value_at(sargs_find(key));
}
SOKOL_API_IMPL const char* sargs_value_def(const char* key, const char* def) {
SOKOL_ASSERT(_sargs.valid && key && def);
int arg_index = sargs_find(key);
if (-1 != arg_index) {
return sargs_value_at(arg_index);
}
else {
return def;
}
}
SOKOL_API_IMPL bool sargs_equals(const char* key, const char* val) {
SOKOL_ASSERT(_sargs.valid && key && val);
return 0 == strcmp(sargs_value(key), val);
}
SOKOL_API_IMPL bool sargs_boolean(const char* key) {
const char* val = sargs_value(key);
return (0 == strcmp("true", val)) ||
(0 == strcmp("yes", val)) ||
(0 == strcmp("on", val));
}
#endif