optparser.c

Sat, 19 Feb 2000 23:23:29 +0100

author
tuomov
date
Sat, 19 Feb 2000 23:23:29 +0100
changeset 1
6e704fc09528
parent 0
86b7f6f9c5c0
child 3
b1fbfab67908
permissions
-rw-r--r--

trunk: changeset 4
- Added include support in config file parser

- Added scatn()

- Fixed remalloczero()

- Fixed is_end() in numparser2.h -- EOF case was missing

/*
 * libtu/optparser.c
 *
 * Copyright (c) Tuomo Valkonen 1999-2000.
 * 
 * This file is distributed under the terms of the "Artistic License".
 * See the included file LICENSE for details.
 */

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

#include "include/util.h"
#include "include/misc.h"
#include "include/optparser.h"
#include "include/output.h"


#define O_CHAINABLE(o)	(o->flags&OPT_CHAINABLE)
#define O_IMM_ARG(o)	(o->flags&OPT__IMM_ARG)
#define O_NO_DASH(o)	(o->flags&OPT_NO_DASH)
#define O_MIDLONG(o)	(o->flags&OPT_MIDLONG)
#define O_NO_LONG(o)	(o->flags&OPT_NO_LONG)
#define O_ARGS(o)		(o->flags&OPT_OPT_ARG)
#define O_ARG(o)		(o->flasg&OPT_ARG)
#define O_OPT_ARG(o)	(O_ARGS(o)==OPT_OPT_ARG)
#define O_ID(o)			(o->optid)


static const OptParserOpt *o_opts=NULL;
static char *const *o_current=NULL;
static int o_left=0;
static const char* o_chain_ptr=NULL;
static int o_args_left=0;
static const char*o_tmp=NULL;
static int o_error=0;


/* */


void optparser_init(int argc, char *const argv[], const OptParserOpt *opts)
{
	o_opts=opts;
	o_current=argv+1;
	o_left=argc-1;
	o_chain_ptr=NULL;
	o_args_left=0;
	o_tmp=NULL;
}


/* */


static const OptParserOpt *find_chain_opt(char p, const OptParserOpt *o)
{
	for(;O_ID(o);o++){
		if(O_CHAINABLE(o) && O_ID(o)==p)
			return o;
	}
	return NULL;
}


static bool valid_chain(const char *p, const OptParserOpt *o)
{
	while(*p!='\0'){
		if(!find_chain_opt(*p, o))
			return FALSE;
		p++;
	}
	return TRUE;
}
	

static bool is_option(const char *p)
{
	 if(p==NULL)
		  return FALSE;
	 if(*p++!='-')
		  return FALSE;
	 if(*p++!='-')
		  return FALSE;
	 if(*p=='\0')
		  return FALSE;
	 return TRUE;
}
	

/* */


int optparser_get_opt()
{
#define RET(X) return o_tmp=p, o_error=X
	const char *p, *p2=NULL;
	bool lo=FALSE, dash=TRUE;
	const OptParserOpt *o;
	int l;

	while(o_args_left)
		optparser_get_arg();

	o_tmp=NULL;

	/* Are we doing a chain (i.e. opt. of style 'tar xzf') ? */
	if(o_chain_ptr!=NULL){
		p=o_chain_ptr++;
		if(!*o_chain_ptr)
			o_chain_ptr=NULL;

		o=find_chain_opt(*p, o_opts);
		
		if(o==NULL)
			RET(E_OPT_INVALID_CHAIN_OPTION);

		goto do_arg;
	}
	
	if(o_left<1)
		return OPT_ID_END;

	o_left--;
	p=*o_current++;
	
	if(*p!='-'){
		/* foo */
		dash=FALSE;
	}else if(*(p+1)=='-'){
		/* --foo */
		if(*(p+2)=='\0'){
			/* -- */
			o_args_left=o_left;
			RET(OPT_ID_ARGUMENT);
		}
		lo=TRUE;
		p2=p+2;
	}else{
		/* -foo */
		if(*(p+1)=='\0'){
			/* - */
			o_args_left=1;
			RET(OPT_ID_ARGUMENT);
		}
		p2=p+1;
	}

	o=o_opts;

again:
	for(;O_ID(o);o++){
		if(lo){
			/* Do long option (--foo=bar) */
			if(o->longopt==NULL)
				continue;
			l=strlen(o->longopt);
			if(strncmp(p2, o->longopt, l)!=0)
				continue;
			
			if(O_NO_LONG(o))
				RET(E_OPT_INVALID_OPTION);

			if(p2[l]=='\0'){
				if(O_ARGS(o)==OPT_ARG)
					 RET(E_OPT_MISSING_ARGUMENT);
				return O_ID(o);
			}else if(p2[l]=='='){
				if(!O_ARGS(o))
					 RET(E_OPT_UNEXPECTED_ARGUMENT);
				if(p2[l+1]=='\0')
					 RET(E_OPT_MISSING_ARGUMENT);
				o_tmp=p2+l+1;
				o_args_left=1;
				return O_ID(o);
			}
		}else{
			/* Do midlong option (-foobar) */
			if(dash && o->longopt!=NULL && strcmp(p2, o->longopt)==0){
				if(!O_MIDLONG(o))
					 RET(E_OPT_INVALID_OPTION);
				goto do_arg;
			}
	
			/* Do short option (-f) */
			if((!dash && !O_NO_DASH(o)) || *p2!=O_ID(o))
				continue;
			   
			if(*(p2+1)!='\0'){
				if(O_CHAINABLE(o) && valid_chain(p2+1, o_opts)){
					 o_chain_ptr=p2+1;
				}else if(O_IMM_ARG(o)){
					 o_tmp=p2+1;
					 o_args_left=1;
					 return O_ID(o);
				}else{
					 continue;
				}
			}
do_arg:
			if(!O_ARGS(o))
				return O_ID(o);
			   
			if(O_ARGS(o)==OPT_ARG){
				if(o_left==0)
					 RET(E_OPT_MISSING_ARGUMENT);
			}else{
				if(!o_left || is_option(*o_current))
					 return O_ID(o);
			}

			o_args_left=1;
			return O_ID(o);
		}
	}

	if(dash)
		RET(E_OPT_INVALID_OPTION);
	
	RET(OPT_ID_ARGUMENT);
#undef RET
}


/* */


const char* optparser_get_arg()
{
	const char *p;
	
	if(o_tmp!=NULL){
		/* If o_args_left==0, then were returning an invalid option
		 * otherwise an immediate argument (e.g. -funsigned-char
		 * where '-f' is the option and 'unsigned-char' the argument)
		 */
		if(o_args_left>0)
			o_args_left--;
		p=o_tmp;
		o_tmp=NULL;
		return p;
	}
		
	if(o_args_left<1 || o_left<1)
		return NULL;

	o_left--;
	o_args_left--;
	return *o_current++;
}


/* */

static void warn_arg(const char *e)
{
	const char*p=optparser_get_arg();
	
	if(p==NULL)
		warn("%s (null)", e);
	else
		warn("%s \'%s\'", e, p);
}


static void warn_opt(const char *e)
{
	if(o_chain_ptr!=NULL && o_tmp!=NULL)
		warn("%s \'-%c\'", e, *o_tmp);
	else
		warn_arg(e);
}


void optparser_print_error()
{
	switch(o_error){
	case E_OPT_INVALID_OPTION:
		warn_opt(TR("Invalid option"));
		break;

	case E_OPT_INVALID_CHAIN_OPTION:
		warn_opt(TR("Invalid option in chain (this cannot happen)"));
		break;		

	case E_OPT_SYNTAX_ERROR:
		warn_arg(TR("Syntax error while parsing"));
		break;
				 
	case E_OPT_MISSING_ARGUMENT:
		warn_opt(TR("Missing argument to"));
		break;

	case E_OPT_UNEXPECTED_ARGUMENT:
		warn_opt(TR("No argument expected to"));
		break;
		
	case OPT_ID_ARGUMENT:
		warn(TR("Unexpected argument"));
		break;

	default:
		warn(TR("(unknown error)"));
	}
	
	o_tmp=NULL;
	o_error=0;
}

mercurial