/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
 * getopt function definitions for NOLIBC, adapted from musl libc
 * Copyright (C) 2005-2020 Rich Felker, et al.
 * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
 */

/* make sure to include all global symbols */
#include "nolibc.h"

#ifndef _NOLIBC_GETOPT_H
#define _NOLIBC_GETOPT_H

struct FILE;
static struct FILE *const stderr;
static int fprintf(struct FILE *stream, const char *fmt, ...);

__attribute__((weak,unused,section(".data.nolibc_getopt")))
char *optarg;

__attribute__((weak,unused,section(".data.nolibc_getopt")))
int optind = 1, opterr = 1, optopt;

static __attribute__((unused))
int getopt(int argc, char * const argv[], const char *optstring)
{
	static int __optpos;
	int i;
	char c, d;
	char *optchar;

	if (!optind) {
		__optpos = 0;
		optind = 1;
	}

	if (optind >= argc || !argv[optind])
		return -1;

	if (argv[optind][0] != '-') {
		if (optstring[0] == '-') {
			optarg = argv[optind++];
			return 1;
		}
		return -1;
	}

	if (!argv[optind][1])
		return -1;

	if (argv[optind][1] == '-' && !argv[optind][2])
		return optind++, -1;

	if (!__optpos)
		__optpos++;
	c = argv[optind][__optpos];
	optchar = argv[optind] + __optpos;
	__optpos++;

	if (!argv[optind][__optpos]) {
		optind++;
		__optpos = 0;
	}

	if (optstring[0] == '-' || optstring[0] == '+')
		optstring++;

	i = 0;
	d = 0;
	do {
		d = optstring[i++];
	} while (d && d != c);

	if (d != c || c == ':') {
		optopt = c;
		if (optstring[0] != ':' && opterr)
			fprintf(stderr, "%s: unrecognized option: %c\n", argv[0], *optchar);
		return '?';
	}
	if (optstring[i] == ':') {
		optarg = NULL;
		if (optstring[i + 1] != ':' || __optpos) {
			optarg = argv[optind++];
			if (__optpos)
				optarg += __optpos;
			__optpos = 0;
		}
		if (optind > argc) {
			optopt = c;
			if (optstring[0] == ':')
				return ':';
			if (opterr)
				fprintf(stderr, "%s: option requires argument: %c\n",
					argv[0], *optchar);
			return '?';
		}
	}
	return c;
}

#endif /* _NOLIBC_GETOPT_H */
