/* argloop.c 2.9.0 92/07/06 - interpret command-line arguments
 *
-----------------------------------------------------------------------
    This software module copyright (c) 1990, 1991 Damian Cugley.  
    It is provided for free on an "as-is" basis.
    See the file COPYING for more information.
    See argloop(3) for more information on this software module.
-----------------------------------------------------------------------
 * 2.0
 *  created - Damian Cugley <pdc@oxford.prg> Fri 12 Jan 1990
 * 2.1
 *  files as well
 * 2.2
 *  Standard initalisiation files
 * 2.3 - pdc Sun 25 Feb 1990
 *  Added the global variable |verbose| to argloop rather than importing
 *  it from the main program.
 * 2.4 - pdc Mon 2 Apr 1990
 *  Added argloop_verbatim
 * 2.5 - pdc Mon 14 May 1990
 *  -vf+qz is same as -v -f +q +z
 * 2.7 - pdc Sat. 2 Mar. 1991
 *  uses simpler file format (less shell-like)
 *  Made properly reentrant (!)
 * 2.7.1 - pdc Tue. 12 Mar. 1991
 *  Put xmalloc in strword.c & added strmisc header file
 * 2.7.2 - pdc Fri. 21 June 1991
 *  Added support for MS-DOS filenames
 */

#include "config.h"
#include "xstdio.h"		/* <stdio.h> plus prototypes */
#include "strmisc.h"		/* inludes <string(s).h> */
#include "argloop.h"

#ifndef NDEBUG
int argloop_debug = 0;
#  define log1(X,A)	if (argloop_debug) fprintf(stderr, X,A)
#else
#  define log1(X,A)
#endif

int	argloop_verbatim = 0;

typedef char *(*Argloop_next_fn)ARGS((void));
     
void
argloop(cxt, opts)
     Argloop_context	*cxt;
     Argloop_options	*opts;
{
  register const char *p;
  int		flag;		/* Boolean */
  const char   *arg;

  if (!cxt) return;
  arg = (cxt->first ? cxt->first : cxt->word(cxt->data));
  
  while (arg)
    {
      log1("%%%%%s\n", arg);
  
      if (argloop_verbatim || !(*arg == '-' || *arg == '+'))
	opts->arg(0, arg);	/* unflagged argument */
      else
	{
	  flag = (*arg == '-');
	  while (*++arg)
	    {
	      for (p = opts->optstring; *p && *p != *arg; p++)
		;		/* search for flag letter */
	      if (!*p)
		{
		  if (*arg == '-' || *arg == '+')
		    flag = (*arg == '-');
		  else
		    {
		      opts->error(*arg); return;
		    }
		}
	      else if (flag && *(p + 1) == ':')
		{		/* this flag wants an argument too */
		  if (*++arg)
		    {		/* use the remainder of this cmd-line arg */
		      opts->arg(*p, arg);
		    }
		  else
		    {		/* get new cmd-line arg */
		      const char *t = cxt->word(cxt->data);

		      log1("%%%%%%\"%s\"\n", (t ? t : "[NIL]") );
		      opts->arg(*p, t);
		      if (!t) 
			{
			  if (cxt->free) cxt->free(cxt->data);
			  return;
			}
		    }
		  break;	/* from inner while loop */
		}
	      else
		opts->flag(*p, flag);
	    }
	}
      arg = cxt->word(cxt->data);
    }
  if (cxt->free) cxt->free(cxt->data);
  xfree((addr)cxt);
}



/*
 *  How to use the above to parse command-line arguments
 */

struct argv { int argc; const char **argv; };

static const char *
argv_word(st)
     struct argv *st;
{
  return ((--st->argc > 0) ? *++st->argv : (char *)0);
}

static void
argv_free(st)
     struct argv *st;
{
  xfree(st);
}

Argloop_context *
al_argv(argc, argv)
     int		argc;
     const char        *argv[];
{
  struct argv *av = (struct argv *)xmalloc(sizeof (struct argv));
  Argloop_context *cxt = (Argloop_context *)xmalloc(sizeof (Argloop_context));
  
  av->argc = argc; av->argv = argv;
  cxt->data = (char *)av; cxt->first = (char *)0;
  cxt->word = argv_word; 
  cxt->free = argv_free;
  return cxt;
}


static const char *
string_word(st)
     char **st;
{
  return strword((char *)0, st);
}

static void
string_free(st)
     char **st;
{
  xfree(st);
}

Argloop_context *
al_string(string)
     char *string;	/* will be destroyed */
{
  Argloop_context *cxt;
  
  if (!string)
    return (Argloop_context *)0;

  cxt = (Argloop_context *)xmalloc(sizeof (Argloop_context));  
  cxt->word = string_word; 
  cxt->free = string_free;
  cxt->data = xmalloc(sizeof (char *));
  cxt->first = strword(string, (char **)cxt->data);
  return cxt;
}
