/*% cyntax -DTEST % && cc -DTEST -go # %
 */
#include "rc.h"
#include "getflags.h"
#include "fns.h"
char *flagset[] = {"<flag>"};
char **flag[NFLAG];
char cmdline[NCMDLINE+1];
char *cmdname;
static char *flagarg="";
static void reverse(char**, char**);
static int scanflag(int, char*);
static void errn(char*, int);
static void errs(char*);
static void errc(int);
static int reason;
#define	RESET	1
#define	FEWARGS	2
#define	FLAGSYN	3
#define	BADFLAG	4
static int badflag;

int
getflags(int argc, char *argv[], char *flags, int stop)
{
	char *s, *t;
	int i, j, c, count;
	flagarg = flags;
	if(cmdname==0)
		cmdname = argv[0];
	s = cmdline;
	for(i = 0;i!=argc;i++){
		for(t = argv[i];*t;t++)
			if(s!=&cmdline[NCMDLINE])
				*s++=*t;
		if(i!=argc-1 && s!=&cmdline[NCMDLINE])
			*s++=' ';
	}
	*s='\0';
	i = 1;
	while(i!=argc){
		if(argv[i][0]!='-' || argv[i][1]=='\0'){
			if(stop)
				return argc;
			i++;
			continue;
		}
		s = argv[i]+1;
		while(*s){
			c=*s++;
			count = scanflag(c, flags);
			if(count==-1)
				return -1;
			if(flag[c]){ reason = RESET; badflag = c; return -1; }
			if(count==0){
				flag[c] = flagset;
				if(*s=='\0'){
					for(j = i+1;j<=argc;j++)
						argv[j-1] = argv[j];
					--argc;
				}
			}
			else{
				if(*s=='\0'){
					for(j = i+1;j<=argc;j++)
						argv[j-1] = argv[j];
					--argc;
					s = argv[i];
				}
				if(argc-i<count){
					reason = FEWARGS;
					badflag = c;
					return -1;
				}
				reverse(argv+i, argv+argc);
				reverse(argv+i, argv+argc-count);
				reverse(argv+argc-count+1, argv+argc);
				argc-=count;
				flag[c] = argv+argc+1;
				flag[c][0] = s;
				s="";
			}
		}
	}
	return argc;
}

static void
reverse(char **p, char **q)
{
	char *t;
	for(;p<q;p++,--q){ t=*p; *p=*q; *q = t; }
}

static int
scanflag(int c, char *f)
{
	int fc, count;
	if(0<=c && c<NFLAG)
		while(*f){
			if(*f==' '){
				f++;
				continue;
			}
			fc=*f++;
			if(*f==':'){
				f++;
				if(*f<'0' || '9'<*f){ reason = FLAGSYN; return -1; }
				count = 0;
				while('0'<=*f && *f<='9') count = count*10+*f++-'0';
			}
			else
				count = 0;
			if(*f=='['){
				do{
					f++;
					if(*f=='\0'){ reason = FLAGSYN; return -1; }
				}while(*f!=']');
				f++;
			}
			if(c==fc)
				return count;
		}
	reason = BADFLAG;
	badflag = c;
	return -1;
}

void
usage(char *tail)
{
	char *s, *t, c;
	int count, nflag = 0;
	switch(reason){
	case RESET:
		errs("Flag -");
		errc(badflag);
		errs(": set twice\n");
		break;
	case FEWARGS:
		errs("Flag -");
		errc(badflag);
		errs(": too few arguments\n");
		break;
	case FLAGSYN:
		errs("Bad argument to getflags!\n");
		break;
	case BADFLAG:
		errs("Illegal flag -");
		errc(badflag);
		errc('\n');
		break;
	}
	errs("Usage: ");
	errs(cmdname);
	for(s = flagarg;*s;){
		c=*s;
		if(*s++==' ')
			continue;
		if(*s==':'){
			s++;
			count = 0;
			while('0'<=*s && *s<='9') count = count*10+*s++-'0';
		}
		else count = 0;
		if(count==0){
			if(nflag==0)
				errs(" [-");
			nflag++;
			errc(c);
		}
		if(*s=='['){
			s++;
			while(*s!=']' && *s!='\0') s++;
			if(*s==']')
				s++;
		}
	}
	if(nflag)
		errs("]");
	for(s = flagarg;*s;){
		c=*s;
		if(*s++==' ')
			continue;
		if(*s==':'){
			s++;
			count = 0;
			while('0'<=*s && *s<='9') count = count*10+*s++-'0';
		}
		else count = 0;
		if(count!=0){
			errs(" [-");
			errc(c);
			if(*s=='['){
				s++;
				t = s;
				while(*s!=']' && *s!='\0') s++;
				errs(" ");
				errn(t, s-t);
				if(*s==']')
					s++;
			}
			else
				while(count--) errs(" arg");
			errs("]");
		}
		else if(*s=='['){
			s++;
			while(*s!=']' && *s!='\0') s++;
			if(*s==']')
				s++;
		}
	}
	if(tail){
		errs(" ");
		errs(tail);
	}
	errs("\n");
	Exit("bad flags");
}

static void
errn(char *s, int count)
{
	while(count){ errc(*s++); --count; }
}

static void
errs(char *s)
{
	while(*s) errc(*s++);
}
#define	NBUF	80
static char buf[NBUF], *bufp = buf;

static void
errc(int c)
{
	*bufp++=c;
	if(bufp==&buf[NBUF] || c=='\n'){
		Write(2, buf, bufp-buf);
		bufp = buf;
	}
}
