/* 
 *  Option Parsing Library (POPT)
 *  Copyright (c) 1998-2002 Red Hat, Inc.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 *  X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 *  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *  Except as contained in this notice, the name of the X Consortium shall not be
 *  used in advertising or otherwise to promote the sale, use or other dealings
 *  in this Software without prior written authorization from the X Consortium.
 *
 *  NOTICE:
 *  This is an automatically generated, stripped down version of the
 *  POPT 1.7 library from Red Hat, Inc. This version is still
 *  distributed under above Open Source license, but Red Hat is no longer
 *  responsible for this version. Contact The OSSP Project instead.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#if defined (__GLIBC__) && defined(__LCLINT__)

extern __const __int32_t *__ctype_tolower;

extern __const __int32_t *__ctype_toupper;

#endif

#include <ctype.h>

#include <errno.h>
#include <fcntl.h>
#include <limits.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>

#ifndef __GNUC__
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
#pragma alloca
#  else
#   ifndef alloca
char *alloca();
#   endif
#  endif
# endif
#elif defined(__GNUC__) && defined(__STRICT_ANSI__)
#define alloca __builtin_alloca
#endif

#include "popt.h"

#ifndef H_POPTINT
#define H_POPTINT

static void *_free(const void *p)
{
	if (p != NULL)
		free((void *)p);
	return NULL;
}

typedef unsigned int __pbm_bits;

#define	__PBM_NBITS		(8 * sizeof (__pbm_bits))
#define	__PBM_IX(d)		((d) / __PBM_NBITS)
#define __PBM_MASK(d)		((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))

typedef struct {
	__pbm_bits bits[1];
} pbm_set;

#define	__PBM_BITS(set)	((set)->bits)

#define	PBM_ALLOC(d)	calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
#define	PBM_FREE(s)	_free(s);
#define PBM_SET(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
#define PBM_CLR(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)

struct optionStackEntry {
	int argc;

	const char **argv;

	pbm_set *argb;
	int next;

	const char *nextArg;

	const char *nextCharArg;

	popt_item currAlias;
	int stuffed;
};

struct popt_context_s {
	struct optionStackEntry optionStack[POPT_OPTION_DEPTH];

	struct optionStackEntry *os;

	const char **leftovers;
	int numLeftovers;
	int nextLeftover;

	const struct popt_option *options;
	int restLeftover;

	const char *appName;

	popt_item aliases;
	int numAliases;
	int flags;

	popt_item execs;
	int numExecs;

	const char **finalArgv;
	int finalArgvCount;
	int finalArgvAlloced;

	popt_item doExec;

	const char *execPath;
	int execAbsolute;

	const char *otherHelp;

	pbm_set *arg_strip;
};

#ifdef HAVE_LIBINTL_H
#include <libintl.h>
#endif

#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
#define _(foo) gettext(foo)
#else
#define _(foo) foo
#endif

#if defined(HAVE_DCGETTEXT) && !defined(__LCLINT__)
#define D_(dom, str) dgettext(dom, str)
#define POPT_(foo) D_("popt", foo)
#else
#define D_(dom, str) str
#define POPT_(foo) foo
#endif

#define N_(foo) foo

#endif

static const char *findProgramPath(const char *argv0)
{
	char *path = getenv("PATH");
	char *pathbuf;
	char *start, *chptr;
	char *buf;

	if (argv0 == NULL)
		return NULL;
	if (strchr(argv0, '/'))
		return strdup(argv0);

	if (path == NULL)
		return NULL;

	start = pathbuf = alloca(strlen(path) + 1);
	buf = malloc(strlen(path) + strlen(argv0) + sizeof ("/"));
	if (buf == NULL)
		return NULL;
	strcpy(pathbuf, path);

	chptr = NULL;

	do {
		if ((chptr = strchr(start, ':')))
			*chptr = '\0';
		sprintf(buf, "%s/%s", start, argv0);

		if (!access(buf, X_OK))
			return buf;

		if (chptr)
			start = chptr + 1;
		else
			start = NULL;
	} while (start && *start);

	free(buf);

	return NULL;
}

#if HAVE_FLOAT_H
#include <float.h>
#endif
#include <math.h>

void popt_setexecpath(popt_context con, const char *path, int allowAbsolute)
{
	con->execPath = _free(con->execPath);
	con->execPath = strdup(path);
	con->execAbsolute = allowAbsolute;
	return;

}

static void invokeCallbacksPRE(popt_context con,
							   const struct popt_option *opt)
{
	if (opt != NULL)
		for (; opt->longName || opt->shortName || opt->arg; opt++) {
			if (opt->arg == NULL)
				continue;
			if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
				invokeCallbacksPRE(con, opt->arg);
			}
			else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
					 (opt->argInfo & POPT_CBFLAG_PRE)) {
				popt_callbacktype cb = (popt_callbacktype) opt->arg;

				cb(con, POPT_CALLBACK_REASON_PRE, NULL, NULL, opt->descrip);

			}
		}
}

static void invokeCallbacksPOST(popt_context con,
								const struct popt_option *opt)
{
	if (opt != NULL)
		for (; opt->longName || opt->shortName || opt->arg; opt++) {
			if (opt->arg == NULL)
				continue;
			if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
				invokeCallbacksPOST(con, opt->arg);
			}
			else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
					 (opt->argInfo & POPT_CBFLAG_POST)) {
				popt_callbacktype cb = (popt_callbacktype) opt->arg;

				cb(con, POPT_CALLBACK_REASON_POST, NULL, NULL, opt->descrip);

			}
		}
}

static void invokeCallbacksOPTION(popt_context con,
								  const struct popt_option *opt,
								  const struct popt_option *myOpt,
								  const void *myData, int shorty)
{
	const struct popt_option *cbopt = NULL;

	if (opt != NULL)
		for (; opt->longName || opt->shortName || opt->arg; opt++) {
			if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
				if (opt->arg != NULL)
					invokeCallbacksOPTION(con, opt->arg, myOpt, myData,
										  shorty);
			}
			else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
					 !(opt->argInfo & POPT_CBFLAG_SKIPOPTION)) {
				cbopt = opt;
			}
			else if (cbopt != NULL &&
					 ((myOpt->shortName && opt->shortName && shorty &&
					   myOpt->shortName == opt->shortName) ||
					  (myOpt->longName && opt->longName &&
					   !strcmp(myOpt->longName, opt->longName)))

				) {
				popt_callbacktype cb = (popt_callbacktype) cbopt->arg;

				const void *cbData =
					(cbopt->descrip ? cbopt->descrip : myData);
				if (cb != NULL) {

					cb(con, POPT_CALLBACK_REASON_OPTION, myOpt,
					   con->os->nextArg, cbData);

				}
				if (!(cbopt->argInfo & POPT_CBFLAG_CONTINUE))
					return;
			}
		}
}

popt_context popt_getcontext(const char *name, int argc, const char **argv,
							 const struct popt_option * options, int flags)
{
	popt_context con = malloc(sizeof (*con));

	if (con == NULL)
		return NULL;
	memset(con, 0, sizeof (*con));

	con->os = con->optionStack;
	con->os->argc = argc;
	con->os->argv = argv;

	con->os->argb = NULL;

	if (!(flags & POPT_CONTEXT_KEEP_FIRST))
		con->os->next = 1;

	con->leftovers = calloc((argc + 1), sizeof (*con->leftovers));
	con->options = options;

	con->aliases = NULL;
	con->numAliases = 0;
	con->flags = flags;
	con->execs = NULL;
	con->numExecs = 0;
	con->finalArgvAlloced = argc * 2;
	con->finalArgv = calloc(con->finalArgvAlloced, sizeof (*con->finalArgv));
	con->execAbsolute = 1;
	con->arg_strip = NULL;

	if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
		con->flags |= POPT_CONTEXT_POSIXMEHARDER;

	if (name) {
		char *t = malloc(strlen(name) + 1);
		if (t)
			con->appName = strcpy(t, name);
	}

	invokeCallbacksPRE(con, con->options);

	return con;
}

static void cleanOSE(struct optionStackEntry *os)
{
	os->nextArg = _free(os->nextArg);
	os->argv = _free(os->argv);
	os->argb = PBM_FREE(os->argb);
}

void popt_resetcontext(popt_context con)
{
	int i;

	if (con == NULL)
		return;
	while (con->os > con->optionStack) {
		cleanOSE(con->os--);
	}
	con->os->argb = PBM_FREE(con->os->argb);
	con->os->currAlias = NULL;
	con->os->nextCharArg = NULL;
	con->os->nextArg = NULL;
	con->os->next = 1;

	con->numLeftovers = 0;
	con->nextLeftover = 0;
	con->restLeftover = 0;
	con->doExec = NULL;

	if (con->finalArgv != NULL)
		for (i = 0; i < con->finalArgvCount; i++) {
			con->finalArgv[i] = _free(con->finalArgv[i]);

		}

	con->finalArgvCount = 0;
	con->arg_strip = PBM_FREE(con->arg_strip);
	return;

}

static int handleExec(popt_context con, const char *longName, char shortName)
{
	popt_item item;
	int i;

	if (con->execs == NULL || con->numExecs <= 0)
		return 0;

	for (i = con->numExecs - 1; i >= 0; i--) {
		item = con->execs + i;
		if (longName && !(item->option.longName &&
						  !strcmp(longName, item->option.longName)))
			continue;
		else if (shortName != item->option.shortName)
			continue;
		break;
	}
	if (i < 0)
		return 0;

	if (con->flags & POPT_CONTEXT_NO_EXEC)
		return 1;

	if (con->doExec == NULL) {
		con->doExec = con->execs + i;
		return 1;
	}

	if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
		con->finalArgvAlloced += 10;
		con->finalArgv = realloc(con->finalArgv,
								 sizeof (*con->finalArgv) *
								 con->finalArgvAlloced);
	}

	i = con->finalArgvCount++;
	if (con->finalArgv != NULL) {
		char *s = malloc((longName ? strlen(longName) : 0) + 3);
		if (s != NULL) {
			if (longName)
				sprintf(s, "--%s", longName);
			else
				sprintf(s, "-%c", shortName);
			con->finalArgv[i] = s;
		}
		else
			con->finalArgv[i] = NULL;
	}

	return 1;

}

static int handleAlias(popt_context con,
					   const char *longName, char shortName,
					   const char *nextCharArg)
{
	popt_item item = con->os->currAlias;
	int rc;
	int i;

	if (item) {
		if (longName && (item->option.longName &&
						 !strcmp(longName, item->option.longName)))
			return 0;
		if (shortName && shortName == item->option.shortName)
			return 0;
	}

	if (con->aliases == NULL || con->numAliases <= 0)
		return 0;

	for (i = con->numAliases - 1; i >= 0; i--) {
		item = con->aliases + i;
		if (longName && !(item->option.longName &&
						  !strcmp(longName, item->option.longName)))
			continue;
		else if (shortName != item->option.shortName)
			continue;
		break;
	}
	if (i < 0)
		return 0;

	if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
		return POPT_ERROR_OPTSTOODEEP;

	if (nextCharArg && *nextCharArg)
		con->os->nextCharArg = nextCharArg;

	con->os++;
	con->os->next = 0;
	con->os->stuffed = 0;
	con->os->nextArg = NULL;
	con->os->nextCharArg = NULL;
	con->os->currAlias = con->aliases + i;
	rc = popt_dupargv(con->os->currAlias->argc, con->os->currAlias->argv,
					  &con->os->argc, &con->os->argv);
	con->os->argb = NULL;

	return (rc ? rc : 1);
}

static int execCommand(popt_context con)
{
	popt_item item = con->doExec;
	const char **argv;
	int argc = 0;
	int rc;

	if (item == NULL)
		return POPT_ERROR_NOARG;

	if (item->argv == NULL || item->argc < 1 ||
		(!con->execAbsolute && strchr(item->argv[0], '/')))
		return POPT_ERROR_NOARG;

	argv = malloc(sizeof (*argv) *
				  (6 + item->argc + con->numLeftovers + con->finalArgvCount));
	if (argv == NULL)
		return POPT_ERROR_MALLOC;

	if (!strchr(item->argv[0], '/') && con->execPath) {
		char *s =
			alloca(strlen(con->execPath) + strlen(item->argv[0]) +
				   sizeof ("/"));
		sprintf(s, "%s/%s", con->execPath, item->argv[0]);
		argv[argc] = s;
	}
	else {
		argv[argc] = findProgramPath(item->argv[0]);
	}
	if (argv[argc++] == NULL)
		return POPT_ERROR_NOARG;

	if (item->argc > 1) {
		memcpy(argv + argc, item->argv + 1,
			   sizeof (*argv) * (item->argc - 1));
		argc += (item->argc - 1);
	}

	if (con->finalArgv != NULL && con->finalArgvCount > 0) {
		memcpy(argv + argc, con->finalArgv,
			   sizeof (*argv) * con->finalArgvCount);
		argc += con->finalArgvCount;
	}

	if (con->leftovers != NULL && con->numLeftovers > 0) {

		memcpy(argv + argc, con->leftovers,
			   sizeof (*argv) * con->numLeftovers);
		argc += con->numLeftovers;
	}

	argv[argc] = NULL;

#ifdef __hpux
	rc = setresuid(getuid(), getuid(), -1);
	if (rc)
		return POPT_ERROR_ERRNO;
#else
#if defined(HAVE_SETUID)
	rc = setuid(getuid());
	if (rc)
		return POPT_ERROR_ERRNO;
#elif defined (HAVE_SETREUID)
	rc = setreuid(getuid(), getuid());
	if (rc)
		return POPT_ERROR_ERRNO;
#else
	;
#endif
#endif

	if (argv[0] == NULL)
		return POPT_ERROR_NOARG;

	rc = execvp(argv[0], (char *const *)argv);

	return POPT_ERROR_ERRNO;
}

static const struct popt_option *findOption(const struct popt_option *opt,
											const char *longName,
											char shortName,
											popt_callbacktype * callback,
											const void **callbackData,
											int singleDash)
{
	const struct popt_option *cb = NULL;

	if (singleDash && !shortName && (longName && *longName == '\0'))
		shortName = '-';

	for (; opt->longName || opt->shortName || opt->arg; opt++) {

		if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
			const struct popt_option *opt2;

			if (opt->arg == NULL)
				continue;
			opt2 = findOption(opt->arg, longName, shortName, callback,
							  callbackData, singleDash);
			if (opt2 == NULL)
				continue;
			if (!(callback && *callback))
				return opt2;
			if (!(callbackData && *callbackData == NULL))
				return opt2;

			*callbackData = opt->descrip;

			return opt2;
		}
		else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
			cb = opt;
		}
		else if (longName && opt->longName &&
				 (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
				 !strcmp(longName, opt->longName))
		{
			break;
		}
		else if (shortName && shortName == opt->shortName) {
			break;
		}
	}

	if (!opt->longName && !opt->shortName)
		return NULL;

	if (callback)
		*callback = NULL;
	if (callbackData)
		*callbackData = NULL;
	if (cb) {
		if (callback)

			*callback = (popt_callbacktype) cb->arg;

		if (!(cb->argInfo & POPT_CBFLAG_INC_DATA)) {
			if (callbackData)
				*callbackData = cb->descrip;

		}
	}

	return opt;
}

static const char *findNextArg(popt_context con,
							   unsigned argx, int delete_arg)
{
	struct optionStackEntry *os = con->os;
	const char *arg;

	do {
		int i;
		arg = NULL;
		while (os->next == os->argc && os > con->optionStack)
			os--;
		if (os->next == os->argc && os == con->optionStack)
			break;
		if (os->argv != NULL)
			for (i = os->next; i < os->argc; i++) {

				if (os->argb && PBM_ISSET(i, os->argb))
					continue;
				if (*os->argv[i] == '-')
					continue;
				if (--argx > 0)
					continue;
				arg = os->argv[i];
				if (delete_arg) {
					if (os->argb == NULL)
						os->argb = PBM_ALLOC(os->argc);
					if (os->argb != NULL)
						PBM_SET(i, os->argb);
				}
				break;

			}
		if (os > con->optionStack)
			os--;
	} while (arg == NULL);
	return arg;
}

static const char *expandNextArg(popt_context con, const char *s)
{
	const char *a = NULL;
	size_t alen;
	char *t, *te;
	size_t tn = strlen(s) + 1;
	char c;

	te = t = malloc(tn);;
	if (t == NULL)
		return NULL;
	while ((c = *s++) != '\0') {
		switch (c) {

			case '!':
				if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
					break;
				if (a == NULL) {
					if ((a = findNextArg(con, 1, 1)) == NULL)
						break;
				}
				s += 3;

				alen = strlen(a);
				tn += alen;
				*te = '\0';
				t = realloc(t, tn);
				te = t + strlen(t);
				strncpy(te, a, alen);
				te += alen;
				continue;
				break;
			default:
				break;
		}
		*te++ = c;
	}
	*te = '\0';
	t = realloc(t, strlen(t) + 1);
	return t;
}

static void poptStripArg(popt_context con, int which)
{

	if (con->arg_strip == NULL)
		con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
	if (con->arg_strip != NULL)
		PBM_SET(which, con->arg_strip);

	return;

}

int poptSaveLong(long *arg, int argInfo, long aLong)
{
	if (arg == NULL || (((unsigned long)arg) & (sizeof (*arg) - 1)))
		return POPT_ERROR_NULLARG;

	if (argInfo & POPT_ARGFLAG_NOT)
		aLong = ~aLong;
	switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
		case 0:
			*arg = aLong;
			break;
		case POPT_ARGFLAG_OR:
			*arg |= aLong;
			break;
		case POPT_ARGFLAG_AND:
			*arg &= aLong;
			break;
		case POPT_ARGFLAG_XOR:
			*arg ^= aLong;
			break;
		default:
			return POPT_ERROR_BADOPERATION;
			break;
	}
	return 0;
}

int poptSaveInt(int *arg, int argInfo, long aLong)
{
	if (arg == NULL || (((unsigned long)arg) & (sizeof (*arg) - 1)))
		return POPT_ERROR_NULLARG;

	if (argInfo & POPT_ARGFLAG_NOT)
		aLong = ~aLong;
	switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
		case 0:
			*arg = aLong;
			break;
		case POPT_ARGFLAG_OR:
			*arg |= aLong;
			break;
		case POPT_ARGFLAG_AND:
			*arg &= aLong;
			break;
		case POPT_ARGFLAG_XOR:
			*arg ^= aLong;
			break;
		default:
			return POPT_ERROR_BADOPERATION;
			break;
	}
	return 0;
}

int popt_getnextopt(popt_context con)
{
	const struct popt_option *opt = NULL;
	int done = 0;

	if (con == NULL)
		return -1;
	while (!done) {
		const char *origOptString = NULL;
		popt_callbacktype cb = NULL;
		const void *cbData = NULL;
		const char *longArg = NULL;
		int canstrip = 0;
		int shorty = 0;

		while (!con->os->nextCharArg && con->os->next == con->os->argc
			   && con->os > con->optionStack) {
			cleanOSE(con->os--);
		}
		if (!con->os->nextCharArg && con->os->next == con->os->argc) {

			invokeCallbacksPOST(con, con->options);

			if (con->doExec)
				return execCommand(con);
			return -1;
		}

		if (!con->os->nextCharArg) {
			char *localOptString, *optString;
			int thisopt;

			if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
				con->os->next++;
				continue;
			}

			thisopt = con->os->next;
			if (con->os->argv != NULL)
				origOptString = con->os->argv[con->os->next++];

			if (origOptString == NULL)
				return POPT_ERROR_BADOPT;

			if (con->restLeftover || *origOptString != '-') {
				if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
					con->restLeftover = 1;
				if (con->flags & POPT_CONTEXT_ARG_OPTS) {
					con->os->nextArg = strdup(origOptString);
					return 0;
				}
				if (con->leftovers != NULL)
					con->leftovers[con->numLeftovers++] = origOptString;
				continue;
			}

			localOptString = optString =
				strcpy(alloca(strlen(origOptString) + 1), origOptString);

			if (optString[0] == '\0')
				return POPT_ERROR_BADOPT;

			if (optString[1] == '-' && !optString[2]) {
				con->restLeftover = 1;
				continue;
			}
			else {
				char *oe;
				int singleDash;

				optString++;
				if (*optString == '-')
					singleDash = 0, optString++;
				else
					singleDash = 1;

				if (handleAlias(con, optString, '\0', NULL))
					continue;

				if (handleExec(con, optString, '\0'))
					continue;

				for (oe = optString; *oe && *oe != '='; oe++) {
				};
				if (*oe == '=') {
					*oe++ = '\0';
					longArg = origOptString + (oe - localOptString);
				}

				opt = findOption(con->options, optString, '\0', &cb, &cbData,
								 singleDash);
				if (!opt && !singleDash)
					return POPT_ERROR_BADOPT;
			}

			if (!opt) {
				con->os->nextCharArg = origOptString + 1;
			}
			else {
				if (con->os == con->optionStack &&
					opt->argInfo & POPT_ARGFLAG_STRIP) {
					canstrip = 1;
					poptStripArg(con, thisopt);
				}
				shorty = 0;
			}
		}

		if (con->os->nextCharArg) {
			origOptString = con->os->nextCharArg;

			con->os->nextCharArg = NULL;

			if (handleAlias(con, NULL, *origOptString, origOptString + 1))
				continue;

			if (handleExec(con, NULL, *origOptString)) {
				origOptString++;
				if (*origOptString != '\0')
					con->os->nextCharArg = origOptString;
				continue;
			}

			opt = findOption(con->options, NULL, *origOptString, &cb,
							 &cbData, 0);
			if (!opt)
				return POPT_ERROR_BADOPT;
			shorty = 1;

			origOptString++;
			if (*origOptString != '\0')
				con->os->nextCharArg = origOptString;
		}

		if (opt == NULL)
			return POPT_ERROR_BADOPT;
		if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
			if (poptSaveInt((int *)opt->arg, opt->argInfo, 1L))
				return POPT_ERROR_BADOPERATION;
		}
		else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
			if (opt->arg) {
				if (poptSaveInt
					((int *)opt->arg, opt->argInfo, (long)opt->val))
					return POPT_ERROR_BADOPERATION;
			}
		}
		else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
			con->os->nextArg = _free(con->os->nextArg);
			if (longArg) {

				longArg = expandNextArg(con, longArg);
				con->os->nextArg = longArg;
			}
			else if (con->os->nextCharArg) {
				longArg = expandNextArg(con, con->os->nextCharArg);
				con->os->nextArg = longArg;
				con->os->nextCharArg = NULL;
			}
			else {
				while (con->os->next == con->os->argc &&
					   con->os > con->optionStack) {
					cleanOSE(con->os--);
				}
				if (con->os->next == con->os->argc) {
					if (!(opt->argInfo & POPT_ARGFLAG_OPTIONAL))
						return POPT_ERROR_NOARG;

					con->os->nextArg = NULL;
				}
				else {

					if (con->os == con->optionStack &&
						(opt->argInfo & POPT_ARGFLAG_STRIP) && canstrip) {
						poptStripArg(con, con->os->next);
					}

					if (con->os->argv != NULL) {
						longArg = con->os->argv[con->os->next++];
						longArg = expandNextArg(con, longArg);
						con->os->nextArg = longArg;
					}
				}
			}
			longArg = NULL;

			if (opt->arg) {
				switch (opt->argInfo & POPT_ARG_MASK) {
					case POPT_ARG_STRING:
						*((const char **)opt->arg) = (con->os->nextArg)
							? strdup(con->os->nextArg) : NULL;
						break;

					case POPT_ARG_INT:
					case POPT_ARG_LONG:
						{
							long aLong = 0;
							char *end;

							if (con->os->nextArg) {
								aLong = strtol(con->os->nextArg, &end, 0);
								if (!(end && *end == '\0'))
									return POPT_ERROR_BADNUMBER;
							}

							if ((opt->argInfo & POPT_ARG_MASK) ==
								POPT_ARG_LONG) {
								if (aLong == LONG_MIN || aLong == LONG_MAX)
									return POPT_ERROR_OVERFLOW;
								if (poptSaveLong
									((long *)opt->arg, opt->argInfo, aLong))
									return POPT_ERROR_BADOPERATION;
							}
							else {
								if (aLong > INT_MAX || aLong < INT_MIN)
									return POPT_ERROR_OVERFLOW;
								if (poptSaveInt
									((int *)opt->arg, opt->argInfo, aLong))
									return POPT_ERROR_BADOPERATION;
							}
						}
						break;

					case POPT_ARG_FLOAT:
					case POPT_ARG_DOUBLE:
						{
							double aDouble = 0.0;
							char *end;

							if (con->os->nextArg) {

								int saveerrno = errno;
								errno = 0;
								aDouble = strtod(con->os->nextArg, &end);
								if (errno == ERANGE)
									return POPT_ERROR_OVERFLOW;
								errno = saveerrno;

								if (*end != '\0')
									return POPT_ERROR_BADNUMBER;
							}

							if ((opt->argInfo & POPT_ARG_MASK) ==
								POPT_ARG_DOUBLE) {
								*((double *)opt->arg) = aDouble;
							}
							else {
#define POPT_ABS(a)	((((a) - 0.0) < DBL_EPSILON) ? -(a) : (a))
								if ((POPT_ABS(aDouble) - FLT_MAX) > DBL_EPSILON)
									return POPT_ERROR_OVERFLOW;
								if ((FLT_MIN - POPT_ABS(aDouble)) > DBL_EPSILON)
									return POPT_ERROR_OVERFLOW;
								*((float *)opt->arg) = aDouble;
							}
						} break;
					default:
						fprintf(stdout,
								POPT_
								("option type (%d) not implemented in popt\n"),
								(opt->argInfo & POPT_ARG_MASK));
						exit(EXIT_FAILURE);
						break;
				}
			}
		}

		if (cb) {

			invokeCallbacksOPTION(con, con->options, opt, cbData, shorty);

		}
		else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
			done = 1;

		if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
			con->finalArgvAlloced += 10;
			con->finalArgv = realloc(con->finalArgv,
									 sizeof (*con->finalArgv) *
									 con->finalArgvAlloced);
		}

		if (con->finalArgv != NULL) {
			char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
			if (s != NULL) {
				if (opt->longName)
					sprintf(s, "%s%s",
							((opt->
							  argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
							opt->longName);
				else
					sprintf(s, "-%c", opt->shortName);
				con->finalArgv[con->finalArgvCount++] = s;
			}
			else
				con->finalArgv[con->finalArgvCount++] = NULL;
		}

		if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) ;
		else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) ;
		else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
			if (con->finalArgv != NULL && con->os->nextArg)
				con->finalArgv[con->finalArgvCount++] =
					strdup(con->os->nextArg);

		}
	}

	return (opt ? opt->val : -1);
}

const char *popt_getoptarg(popt_context con)
{
	const char *ret = NULL;

	if (con) {
		ret = con->os->nextArg;
		con->os->nextArg = NULL;
	}

	return ret;
}

const char *popt_getarg(popt_context con)
{
	const char *ret = NULL;
	if (con && con->leftovers != NULL
		&& con->nextLeftover < con->numLeftovers)
		ret = con->leftovers[con->nextLeftover++];
	return ret;
}

const char *popt_peekarg(popt_context con)
{
	const char *ret = NULL;
	if (con && con->leftovers != NULL
		&& con->nextLeftover < con->numLeftovers)
		ret = con->leftovers[con->nextLeftover];
	return ret;
}

const char **popt_getargs(popt_context con)
{
	if (con == NULL ||
		con->leftovers == NULL || con->numLeftovers == con->nextLeftover)
		return NULL;

	con->leftovers[con->numLeftovers] = NULL;

	return (con->leftovers + con->nextLeftover);

}

popt_context popt_freecontext(popt_context con)
{
	popt_item item;
	int i;

	if (con == NULL)
		return con;
	popt_resetcontext(con);
	con->os->argb = _free(con->os->argb);

	if (con->aliases != NULL)
		for (i = 0; i < con->numAliases; i++) {
			item = con->aliases + i;

			item->option.longName = _free(item->option.longName);
			item->option.descrip = _free(item->option.descrip);
			item->option.argDescrip = _free(item->option.argDescrip);

			item->argv = _free(item->argv);
		}
	con->aliases = _free(con->aliases);

	if (con->execs != NULL)
		for (i = 0; i < con->numExecs; i++) {
			item = con->execs + i;

			item->option.longName = _free(item->option.longName);
			item->option.descrip = _free(item->option.descrip);
			item->option.argDescrip = _free(item->option.argDescrip);

			item->argv = _free(item->argv);
		}
	con->execs = _free(con->execs);

	con->leftovers = _free(con->leftovers);
	con->finalArgv = _free(con->finalArgv);
	con->appName = _free(con->appName);
	con->otherHelp = _free(con->otherHelp);
	con->execPath = _free(con->execPath);
	con->arg_strip = PBM_FREE(con->arg_strip);

	con = _free(con);
	return con;
}

int popt_addalias(popt_context con, struct popt_alias alias, int flags)
{
	popt_item item = alloca(sizeof (*item));
	memset(item, 0, sizeof (*item));
	item->option.longName = alias.longName;
	item->option.shortName = alias.shortName;
	item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
	item->option.arg = 0;
	item->option.val = 0;
	item->option.descrip = NULL;
	item->option.argDescrip = NULL;
	item->argc = alias.argc;
	item->argv = alias.argv;
	return popt_additem(con, item, 0);
}

int popt_additem(popt_context con, popt_item newItem, int flags)
{
	popt_item *items, item;
	int *nitems;

	switch (flags) {
		case 1:
			items = &con->execs;
			nitems = &con->numExecs;
			break;
		case 0:
			items = &con->aliases;
			nitems = &con->numAliases;
			break;
		default:
			return 1;
			break;
	}

	*items = realloc((*items), ((*nitems) + 1) * sizeof (**items));
	if ((*items) == NULL)
		return 1;

	item = (*items) + (*nitems);

	item->option.longName =
		(newItem->option.longName ? strdup(newItem->option.longName) : NULL);
	item->option.shortName = newItem->option.shortName;
	item->option.argInfo = newItem->option.argInfo;
	item->option.arg = newItem->option.arg;
	item->option.val = newItem->option.val;
	item->option.descrip =
		(newItem->option.descrip ? strdup(newItem->option.descrip) : NULL);
	item->option.argDescrip =
		(newItem->option.
		 argDescrip ? strdup(newItem->option.argDescrip) : NULL);
	item->argc = newItem->argc;
	item->argv = newItem->argv;

	(*nitems)++;

	return 0;
}

const char *popt_badoption(popt_context con, int flags)
{
	struct optionStackEntry *os = NULL;

	if (con != NULL)
		os = (flags & POPT_BADOPTION_NOALIAS) ? con->optionStack : con->os;

	return (os && os->argv ? os->argv[os->next - 1] : NULL);

}

const char *const popt_strerror(const int error)
{
	switch (error) {
		case POPT_ERROR_NOARG:
			return POPT_("missing argument");
		case POPT_ERROR_BADOPT:
			return POPT_("unknown option");
		case POPT_ERROR_BADOPERATION:
			return POPT_("mutually exclusive logical operations requested");
		case POPT_ERROR_NULLARG:
			return POPT_("opt->arg should not be NULL");
		case POPT_ERROR_OPTSTOODEEP:
			return POPT_("aliases nested too deeply");
		case POPT_ERROR_BADQUOTE:
			return POPT_("error in parameter quoting");
		case POPT_ERROR_BADNUMBER:
			return POPT_("invalid numeric value");
		case POPT_ERROR_OVERFLOW:
			return POPT_("number too large or too small");
		case POPT_ERROR_MALLOC:
			return POPT_("memory allocation failed");
		case POPT_ERROR_ERRNO:
			return strerror(errno);
		default:
			return POPT_("unknown error");
	}
}

int popt_stuffargs(popt_context con, const char **argv)
{
	int argc;
	int rc;

	if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
		return POPT_ERROR_OPTSTOODEEP;

	for (argc = 0; argv[argc]; argc++) {
	};

	con->os++;
	con->os->next = 0;
	con->os->nextArg = NULL;
	con->os->nextCharArg = NULL;
	con->os->currAlias = NULL;
	rc = popt_dupargv(argc, argv, &con->os->argc, &con->os->argv);
	con->os->argb = NULL;
	con->os->stuffed = 1;

	return rc;
}

const char *popt_getinvocationname(popt_context con)
{
	return (con->os->argv ? con->os->argv[0] : "");
}

int popt_strippedargv(popt_context con, int argc, char **argv)
{
	int numargs = argc;
	int j = 1;
	int i;

	if (con->arg_strip)
		for (i = 1; i < argc; i++) {
			if (PBM_ISSET(i, con->arg_strip))
				numargs--;
		}

	for (i = 1; i < argc; i++) {
		if (con->arg_strip && PBM_ISSET(i, con->arg_strip))
			continue;
		argv[j] = (j < numargs) ? argv[i] : NULL;
		j++;
	}

	return numargs;
}

#define POPT_ARGV_ARRAY_GROW_DELTA 5

int popt_dupargv(int argc, const char **argv,
				 int *argcPtr, const char ***argvPtr)
{
	size_t nb = (argc + 1) * sizeof (*argv);
	const char **argv2;
	char *dst;
	int i;

	if (argc <= 0 || argv == NULL)
		return POPT_ERROR_NOARG;
	for (i = 0; i < argc; i++) {
		if (argv[i] == NULL)
			return POPT_ERROR_NOARG;
		nb += strlen(argv[i]) + 1;
	}

	dst = malloc(nb);
	if (dst == NULL)
		return POPT_ERROR_MALLOC;
	argv2 = (void *)dst;
	dst += (argc + 1) * sizeof (*argv);

	for (i = 0; i < argc; i++) {
		argv2[i] = dst;
		dst += strlen(strcpy(dst, argv[i])) + 1;
	}

	argv2[argc] = NULL;

	if (argvPtr) {
		*argvPtr = argv2;
	}
	else {
		free(argv2);
		argv2 = NULL;
	}
	if (argcPtr)
		*argcPtr = argc;
	return 0;
}

int popt_parseargvstring(const char *s, int *argcPtr, const char ***argvPtr)
{
	const char *src;
	char quote = '\0';
	int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
	const char **argv = malloc(sizeof (*argv) * argvAlloced);
	int argc = 0;
	int buflen = strlen(s) + 1;
	char *buf = memset(alloca(buflen), 0, buflen);
	int rc = POPT_ERROR_MALLOC;

	if (argv == NULL)
		return rc;
	argv[argc] = buf;

	for (src = s; *src != '\0'; src++) {
		if (quote == *src) {
			quote = '\0';
		}
		else if (quote != '\0') {
			if (*src == '\\') {
				src++;
				if (!*src) {
					rc = POPT_ERROR_BADQUOTE;
					goto exit;
				}
				if (*src != quote)
					*buf++ = '\\';
			}
			*buf++ = *src;
		}
		else if (isspace(*src)) {
			if (*argv[argc] != '\0') {
				buf++, argc++;
				if (argc == argvAlloced) {
					argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
					argv = realloc(argv, sizeof (*argv) * argvAlloced);
					if (argv == NULL)
						goto exit;
				}
				argv[argc] = buf;
			}
		}
		else
			switch (*src) {
				case '"':
				case '\'':
					quote = *src;
					break;
				case '\\':
					src++;
					if (!*src) {
						rc = POPT_ERROR_BADQUOTE;
						goto exit;
					}

				default:
					*buf++ = *src;
					break;
			}
	}

	if (strlen(argv[argc])) {
		argc++, buf++;
	}

	rc = popt_dupargv(argc, argv, argcPtr, argvPtr);

  exit:
	if (argv)
		free(argv);
	return rc;
}

int poptConfigFileToString(FILE * fp, char **argstrp, int flags)
{
	char line[999];
	char *argstr;
	char *p;
	char *q;
	char *x;
	int t;
	int argvlen = 0;
	size_t maxlinelen = sizeof (line);
	size_t linelen;
	int maxargvlen = 480;
	int linenum = 0;

	*argstrp = NULL;

	if (fp == NULL)
		return POPT_ERROR_NULLARG;

	argstr = calloc(maxargvlen, sizeof (*argstr));
	if (argstr == NULL)
		return POPT_ERROR_MALLOC;

	while (fgets(line, (int)maxlinelen, fp) != NULL) {
		linenum++;
		p = line;

		while (*p != '\0' && isspace(*p))
			p++;

		linelen = strlen(p);
		if (linelen >= maxlinelen - 1)
			return POPT_ERROR_OVERFLOW;

		if (*p == '\0' || *p == '\n')
			continue;
		if (*p == '#')
			continue;

		q = p;

		while (*q != '\0' && (!isspace(*q)) && *q != '=')
			q++;

		if (isspace(*q)) {
			*q++ = '\0';
			while (*q != '\0' && isspace((int)*q))
				q++;
		}
		if (*q == '\0') {
			q[-1] = '\0';
			argvlen += (t = q - p) + (sizeof (" --") - 1);
			if (argvlen >= maxargvlen) {
				maxargvlen = (t > maxargvlen) ? t * 2 : maxargvlen * 2;
				argstr = realloc(argstr, maxargvlen);
				if (argstr == NULL)
					return POPT_ERROR_MALLOC;
			}
			strcat(argstr, " --");
			strcat(argstr, p);
			continue;
		}
		if (*q != '=')
			continue;

		*q++ = '\0';

		while (*q != '\0' && isspace(*q))
			q++;
		if (*q == '\0')
			continue;

		x = p + linelen;
		while (isspace(*--x))
			*x = 0;

		t = x - p;
		argvlen += t + (sizeof ("' --='") - 1);
		if (argvlen >= maxargvlen) {
			maxargvlen = (t > maxargvlen) ? t * 2 : maxargvlen * 2;
			argstr = realloc(argstr, maxargvlen);
			if (argstr == NULL)
				return POPT_ERROR_MALLOC;
		}
		strcat(argstr, " --");
		strcat(argstr, p);
		strcat(argstr, "=\"");
		strcat(argstr, q);
		strcat(argstr, "\"");
	}

	*argstrp = argstr;
	return 0;
}

static void configLine(popt_context con, char *line)
{

	int nameLength = strlen(con->appName);

	const char *entryType;
	const char *opt;
	popt_item item = alloca(sizeof (*item));
	int i, j;

	memset(item, 0, sizeof (*item));

	if (strncmp(line, con->appName, nameLength))
		return;

	line += nameLength;
	if (*line == '\0' || !isspace(*line))
		return;

	while (*line != '\0' && isspace(*line))
		line++;
	entryType = line;
	while (*line == '\0' || !isspace(*line))
		line++;
	*line++ = '\0';

	while (*line != '\0' && isspace(*line))
		line++;
	if (*line == '\0')
		return;
	opt = line;
	while (*line == '\0' || !isspace(*line))
		line++;
	*line++ = '\0';

	while (*line != '\0' && isspace(*line))
		line++;
	if (*line == '\0')
		return;

	if (opt[0] == '-' && opt[1] == '-')
		item->option.longName = opt + 2;
	else if (opt[0] == '-' && opt[2] == '\0')
		item->option.shortName = opt[1];

	if (popt_parseargvstring(line, &item->argc, &item->argv))
		return;

	item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
	for (i = 0, j = 0; i < item->argc; i++, j++) {
		const char *f;
		if (!strncmp
			(item->argv[i], "--POPTdesc=", sizeof ("--POPTdesc=") - 1)) {
			f = item->argv[i] + sizeof ("--POPTdesc=");
			if (f[0] == '$' && f[1] == '"')
				f++;
			item->option.descrip = f;
			item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
			j--;
		}
		else if (!strncmp
				 (item->argv[i], "--POPTargs=", sizeof ("--POPTargs=") - 1)) {
			f = item->argv[i] + sizeof ("--POPTargs=");
			if (f[0] == '$' && f[1] == '"')
				f++;
			item->option.argDescrip = f;
			item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
			item->option.argInfo |= POPT_ARG_STRING;
			j--;
		}
		else if (j != i)
			item->argv[j] = item->argv[i];
	}
	if (j != i) {
		item->argv[j] = NULL;
		item->argc = j;
	}

	if (!strcmp(entryType, "alias"))
		(void)popt_additem(con, item, 0);
	else if (!strcmp(entryType, "exec"))
		(void)popt_additem(con, item, 1);

}

int popt_readconfigfile(popt_context con, const char *fn)
{
	const char *file, *chptr, *end;
	char *buf;
	char *dst;
	int fd, rc;
	off_t fileLength;

	fd = open(fn, O_RDONLY);
	if (fd < 0)
		return (errno == ENOENT ? 0 : POPT_ERROR_ERRNO);

	fileLength = lseek(fd, 0, SEEK_END);
	if (fileLength == -1 || lseek(fd, 0, 0) == -1) {
		rc = errno;
		(void)close(fd);

		errno = rc;

		return POPT_ERROR_ERRNO;
	}

	file = alloca(fileLength + 1);
	if (read(fd, (char *)file, fileLength) != fileLength) {
		rc = errno;
		(void)close(fd);

		errno = rc;

		return POPT_ERROR_ERRNO;
	}
	if (close(fd) == -1)
		return POPT_ERROR_ERRNO;

	dst = buf = alloca(fileLength + 1);

	chptr = file;
	end = (file + fileLength);
	while (chptr < end) {
		switch (*chptr) {
			case '\n':
				*dst = '\0';
				dst = buf;
				while (*dst && isspace(*dst))
					dst++;
				if (*dst && *dst != '#')
					configLine(con, dst);
				chptr++;
				break;
			case '\\':
				*dst++ = *chptr++;
				if (chptr < end) {
					if (*chptr == '\n')
						dst--, chptr++;
					else
						*dst++ = *chptr++;
				}
				break;
			default:
				*dst++ = *chptr++;
				break;
		}
	}

	return 0;
}

int popt_readdefaultconfig(popt_context con, int useEnv)
{
	char *fn, *home;
	int rc;

	if (!con->appName)
		return 0;

	rc = popt_readconfigfile(con, "/etc/popt");
	if (rc)
		return rc;
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
	if (getuid() != geteuid())
		return 0;
#endif

	if ((home = getenv("HOME"))) {
		fn = alloca(strlen(home) + 20);
		strcpy(fn, home);
		strcat(fn, "/.popt");
		rc = popt_readconfigfile(con, fn);
		if (rc)
			return rc;
	}

	return 0;
}

static void displayArgs(popt_context con,
						enum popt_callbackreason foo,
						struct popt_option *key, const char *arg, void *data)
{
	if (key->shortName == '?')
		popt_printhelp(con, stdout, 0);
	else
		popt_printusage(con, stdout, 0);
	exit(0);
}

struct popt_option popt_aliasOptions[] = {
	POPT_TABLEEND
};

struct popt_option popt_helpoptions[] = {
	{NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL},
	{"help", '?', 0, NULL, '?', N_("Show this help message"), NULL},
	{"usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL},

	POPT_TABLEEND
};

static const char *const
getTableTranslationDomain(const struct popt_option *table)
{
	const struct popt_option *opt;

	if (table != NULL)
		for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
			if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
				return opt->arg;
		}
	return NULL;
}

static const char *const
getArgDescrip(const struct popt_option *opt, const char *translation_domain)
{
	if (!(opt->argInfo & POPT_ARG_MASK))
		return NULL;

	if (opt == (popt_helpoptions + 1) || opt == (popt_helpoptions + 2))
		if (opt->argDescrip)
			return POPT_(opt->argDescrip);

	if (opt->argDescrip)
		return D_(translation_domain, opt->argDescrip);

	switch (opt->argInfo & POPT_ARG_MASK) {
		case POPT_ARG_NONE:
			return POPT_("NONE");
#ifdef	DYING
		case POPT_ARG_VAL:
			return POPT_("VAL");
#else
		case POPT_ARG_VAL:
			return NULL;
#endif
		case POPT_ARG_INT:
			return POPT_("INT");
		case POPT_ARG_LONG:
			return POPT_("LONG");
		case POPT_ARG_STRING:
			return POPT_("STRING");
		case POPT_ARG_FLOAT:
			return POPT_("FLOAT");
		case POPT_ARG_DOUBLE:
			return POPT_("DOUBLE");
		default:
			return POPT_("ARG");
	}
}

static char *singleOptionDefaultValue(int lineLength,
									  const struct popt_option *opt,
									  const char *translation_domain)
{
	const char *defstr = D_(translation_domain, "default");
	char *le = malloc(4 * lineLength + 1);
	char *l = le;

	if (le == NULL)
		return NULL;

	*le = '\0';
	*le++ = '(';
	strcpy(le, defstr);
	le += strlen(le);
	*le++ = ':';
	*le++ = ' ';
	if (opt->arg)
		switch (opt->argInfo & POPT_ARG_MASK) {
			case POPT_ARG_VAL:
			case POPT_ARG_INT:
				{
					long aLong = *((int *)opt->arg);
					le += sprintf(le, "%ld", aLong);
				} break;
			case POPT_ARG_LONG:
				{
					long aLong = *((long *)opt->arg);
					le += sprintf(le, "%ld", aLong);
				} break;
			case POPT_ARG_FLOAT:
				{
					double aDouble = *((float *)opt->arg);
					le += sprintf(le, "%g", aDouble);
				} break;
			case POPT_ARG_DOUBLE:
				{
					double aDouble = *((double *)opt->arg);
					le += sprintf(le, "%g", aDouble);
				} break;
			case POPT_ARG_STRING:
				{
					const char *s = *(const char **)opt->arg;
					if (s == NULL) {
						strcpy(le, "null");
						le += strlen(le);
					}
					else {
						size_t slen =
							4 * lineLength - (le - l) - sizeof ("\"...\")");
						*le++ = '"';
						strncpy(le, s, slen);
						le[slen] = '\0';
						le += strlen(le);
						if (slen < strlen(s)) {
							strcpy(le, "...");
							le += strlen(le);
						}
						*le++ = '"';
					}
				}
				break;
			case POPT_ARG_NONE:
			default:
				l = _free(l);
				return NULL;
				break;
		}
	*le++ = ')';
	*le = '\0';

	return l;
}

static void singleOptionHelp(FILE * fp, int maxLeftCol,
							 const struct popt_option *opt,
							 const char *translation_domain)
{
	int indentLength = maxLeftCol + 5;
	int lineLength = 79 - indentLength;
	const char *help = D_(translation_domain, opt->descrip);
	const char *argDescrip = getArgDescrip(opt, translation_domain);
	int helpLength;
	char *defs = NULL;
	char *left;
	int nb = maxLeftCol + 1;

	if (opt->longName)
		nb += strlen(opt->longName);
	if (argDescrip)
		nb += strlen(argDescrip);

	left = malloc(nb);
	if (left == NULL)
		return;
	left[0] = '\0';
	left[maxLeftCol] = '\0';

	if (opt->longName && opt->shortName)
		sprintf(left, "-%c, %s%s", opt->shortName,
				((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
				opt->longName);
	else if (opt->shortName != '\0')
		sprintf(left, "-%c", opt->shortName);
	else if (opt->longName)
		sprintf(left, "%s%s",
				((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
				opt->longName);
	if (!*left)
		goto out;

	if (argDescrip) {
		char *le = left + strlen(left);

		if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
			*le++ = '[';

		if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) {
			defs =
				singleOptionDefaultValue(lineLength, opt, translation_domain);
			if (defs) {
				char *t = malloc((help ? strlen(help) : 0) +
								 strlen(defs) + sizeof (" "));
				if (t) {
					char *te = t;
					*te = '\0';
					if (help) {
						strcpy(te, help);
						te += strlen(te);
					}
					*te++ = ' ';
					strcpy(te, defs);
					defs = _free(defs);
				}
				defs = t;
			}
		}

		if (opt->argDescrip == NULL) {
			switch (opt->argInfo & POPT_ARG_MASK) {
				case POPT_ARG_NONE:
					break;
				case POPT_ARG_VAL:
#ifdef	NOTNOW
					{
						long aLong = opt->val;
						int ops = (opt->argInfo & POPT_ARGFLAG_LOGICALOPS);
						int negate = (opt->argInfo & POPT_ARGFLAG_NOT);

						if (!ops
							&& (aLong == 0L || aLong == 1L || aLong == -1L))
							break;
						*le++ = '[';
						switch (ops) {
							case POPT_ARGFLAG_OR:
								*le++ = '|';
								break;
							case POPT_ARGFLAG_AND:
								*le++ = '&';
								break;
							case POPT_ARGFLAG_XOR:
								*le++ = '^';
								break;
							default:
								break;
						}
						*le++ = '=';
						if (negate)
							*le++ = '~';

						le += sprintf(le, (ops ? "0x%lx" : "%ld"), aLong);

						*le++ = ']';
					}
#endif
					break;
				case POPT_ARG_INT:
				case POPT_ARG_LONG:
				case POPT_ARG_FLOAT:
				case POPT_ARG_DOUBLE:
				case POPT_ARG_STRING:
					*le++ = '=';
					strcpy(le, argDescrip);
					le += strlen(le);
					break;
				default:
					break;
			}
		}
		else {
			*le++ = '=';
			strcpy(le, argDescrip);
			le += strlen(le);
		}
		if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
			*le++ = ']';
		*le = '\0';
	}

	if (help)
		fprintf(fp, "  %-*s   ", maxLeftCol, left);
	else {
		fprintf(fp, "  %s\n", left);
		goto out;
	}

	left = _free(left);
	if (defs) {
		help = defs;
		defs = NULL;
	}

	helpLength = strlen(help);

	while (helpLength > lineLength) {
		const char *ch;
		char format[16];

		ch = help + lineLength - 1;
		while (ch > help && !isspace(*ch))
			ch--;
		if (ch == help)
			break;
		while (ch > (help + 1) && isspace(*ch))
			ch--;
		ch++;

		sprintf(format, "%%.%ds\n%%%ds", (int)(ch - help), indentLength);

		fprintf(fp, format, help, " ");

		help = ch;
		while (isspace(*help) && *help)
			help++;
		helpLength = strlen(help);
	}

	if (helpLength)
		fprintf(fp, "%s\n", help);

  out:

	defs = _free(defs);

	left = _free(left);
}

static int maxArgWidth(const struct popt_option *opt,
					   const char *translation_domain)
{
	int max = 0;
	int len = 0;
	const char *s;

	if (opt != NULL)
		while (opt->longName || opt->shortName || opt->arg) {
			if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
				if (opt->arg)
					len = maxArgWidth(opt->arg, translation_domain);
				if (len > max)
					max = len;
			}
			else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
				len = sizeof ("  ") - 1;
				if (opt->shortName != '\0')
					len += sizeof ("-X") - 1;
				if (opt->shortName != '\0' && opt->longName)
					len += sizeof (", ") - 1;
				if (opt->longName) {
					len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH)
							? sizeof ("-") - 1 : sizeof ("--") - 1);
					len += strlen(opt->longName);
				}

				s = getArgDescrip(opt, translation_domain);
				if (s)
					len += sizeof ("=") - 1 + strlen(s);
				if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
					len += sizeof ("[]") - 1;
				if (len > max)
					max = len;
			}

			opt++;
		}

	return max;
}

static void itemHelp(FILE * fp,
					 popt_item items, int nitems, int left,
					 const char *translation_domain)
{
	popt_item item;
	int i;

	if (items != NULL)
		for (i = 0, item = items; i < nitems; i++, item++) {
			const struct popt_option *opt;
			opt = &item->option;
			if ((opt->longName || opt->shortName) &&
				!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
				singleOptionHelp(fp, left, opt, translation_domain);
		}
}

static void singleTableHelp(popt_context con, FILE * fp,
							const struct popt_option *table, int left,
							const char *translation_domain)
{
	const struct popt_option *opt;
	const char *sub_transdom;

	if (table == popt_aliasOptions) {
		itemHelp(fp, con->aliases, con->numAliases, left, NULL);
		itemHelp(fp, con->execs, con->numExecs, left, NULL);
		return;
	}

	if (table != NULL)
		for (opt = table; (opt->longName || opt->shortName || opt->arg);
			 opt++) {
			if ((opt->longName || opt->shortName)
				&& !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
				singleOptionHelp(fp, left, opt, translation_domain);
		}

	if (table != NULL)
		for (opt = table; (opt->longName || opt->shortName || opt->arg);
			 opt++) {
			if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE)
				continue;
			sub_transdom = getTableTranslationDomain(opt->arg);
			if (sub_transdom == NULL)
				sub_transdom = translation_domain;

			if (opt->descrip)
				fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));

			singleTableHelp(con, fp, opt->arg, left, sub_transdom);
		}
}

static int showHelpIntro(popt_context con, FILE * fp)
{
	int len = 6;
	const char *fn;

	fprintf(fp, POPT_("Usage:"));
	if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {

		fn = con->optionStack->argv[0];

		if (fn == NULL)
			return len;
		if (strchr(fn, '/'))
			fn = strrchr(fn, '/') + 1;
		fprintf(fp, " %s", fn);
		len += strlen(fn) + 1;
	}

	return len;
}

void popt_printhelp(popt_context con, FILE * fp, int flags)
{
	int leftColWidth;

	(void)showHelpIntro(con, fp);
	if (con->otherHelp)
		fprintf(fp, " %s\n", con->otherHelp);
	else
		fprintf(fp, " %s\n", POPT_("[OPTION...]"));

	leftColWidth = maxArgWidth(con->options, NULL);
	singleTableHelp(con, fp, con->options, leftColWidth, NULL);
}

static int singleOptionUsage(FILE * fp, int cursor,
							 const struct popt_option *opt,
							 const char *translation_domain)
{
	int len = 4;
	char shortStr[2] = { '\0', '\0' };
	const char *item = shortStr;
	const char *argDescrip = getArgDescrip(opt, translation_domain);

	if (opt->shortName != '\0' && opt->longName != NULL) {
		len += 2;
		if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH))
			len++;
		len += strlen(opt->longName);
	}
	else if (opt->shortName != '\0') {
		len++;
		shortStr[0] = opt->shortName;
		shortStr[1] = '\0';
	}
	else if (opt->longName) {
		len += strlen(opt->longName);
		if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH))
			len++;
		item = opt->longName;
	}

	if (len == 4)
		return cursor;

	if (argDescrip)
		len += strlen(argDescrip) + 1;

	if ((cursor + len) > 79) {
		fprintf(fp, "\n       ");
		cursor = 7;
	}

	if (opt->longName && opt->shortName) {
		fprintf(fp, " [-%c|-%s%s%s%s]",
				opt->shortName,
				((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "" : "-"),
				opt->longName, (argDescrip ? " " : ""),
				(argDescrip ? argDescrip : ""));
	}
	else {
		fprintf(fp, " [-%s%s%s%s]",
				((opt->shortName
				  || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"), item,
				(argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""),
				(argDescrip ? argDescrip : ""));
	}

	return cursor + len + 1;
}

static int itemUsage(FILE * fp, int cursor, popt_item item, int nitems,
					 const char *translation_domain)
{
	int i;

	if (item != NULL)
		for (i = 0; i < nitems; i++, item++) {
			const struct popt_option *opt;
			opt = &item->option;
			if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
				translation_domain = (const char *)opt->arg;
			}
			else if ((opt->longName || opt->shortName) &&
					 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
				cursor =
					singleOptionUsage(fp, cursor, opt, translation_domain);
			}
		}

	return cursor;
}

typedef struct poptDone_s {
	int nopts;
	int maxopts;
	const void **opts;
}  *poptDone;

static int singleTableUsage(popt_context con, FILE * fp, int cursor,
							const struct popt_option *opt,
							const char *translation_domain, poptDone done)
{
	if (opt != NULL)
		for (; (opt->longName || opt->shortName || opt->arg); opt++) {
			if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
				translation_domain = (const char *)opt->arg;
			}
			else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
				if (done) {
					int i = 0;
					for (i = 0; i < done->nopts; i++) {

						const void *that = done->opts[i];

						if (that == NULL || that != opt->arg)
							continue;
						break;
					}
					if (opt->arg == NULL || i < done->nopts)
						continue;

					if (done->nopts < done->maxopts)
						done->opts[done->nopts++] = (const void *)opt->arg;

				}
				cursor = singleTableUsage(con, fp, cursor, opt->arg,
										  translation_domain, done);
			}
			else if ((opt->longName || opt->shortName) &&
					 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
				cursor =
					singleOptionUsage(fp, cursor, opt, translation_domain);
			}
		}

	return cursor;
}

static int showShortOptions(const struct popt_option *opt, FILE * fp,
							char *str)
{
	char *s = alloca(300);

	s[0] = '\0';
	if (str == NULL) {
		memset(s, 0, sizeof (s));
		str = s;
	}

	if (opt != NULL)
		for (; (opt->longName || opt->shortName || opt->arg); opt++) {
			if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
				str[strlen(str)] = opt->shortName;
			else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
				if (opt->arg)
					(void)showShortOptions(opt->arg, fp, str);
		}

	if (s != str || *s != '\0')
		return 0;

	fprintf(fp, " [-%s]", s);
	return strlen(s) + 4;
}

void popt_printusage(popt_context con, FILE * fp, int flags)
{
	poptDone done = memset(alloca(sizeof (*done)), 0, sizeof (*done));
	int cursor;

	done->nopts = 0;
	done->maxopts = 64;
	cursor = done->maxopts * sizeof (*done->opts);

	done->opts = memset(alloca(cursor), 0, cursor);
	done->opts[done->nopts++] = (const void *)con->options;

	cursor = showHelpIntro(con, fp);
	cursor += showShortOptions(con->options, fp, NULL);
	cursor = singleTableUsage(con, fp, cursor, con->options, NULL, done);
	cursor = itemUsage(fp, cursor, con->aliases, con->numAliases, NULL);
	cursor = itemUsage(fp, cursor, con->execs, con->numExecs, NULL);

	if (con->otherHelp) {
		cursor += strlen(con->otherHelp) + 1;
		if (cursor > 79)
			fprintf(fp, "\n       ");
		fprintf(fp, " %s", con->otherHelp);
	}

	fprintf(fp, "\n");
}

void popt_setotheroptionhelp(popt_context con, const char *text)
{
	con->otherHelp = _free(con->otherHelp);
	con->otherHelp = strdup(text);
}
