optparser.c

changeset 0
86b7f6f9c5c0
child 3
b1fbfab67908
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/optparser.c	Tue Feb 15 18:57:52 2005 +0100
@@ -0,0 +1,308 @@
+/*
+ * 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