This file is complete.def, from which is created complete.c.
It implements the builtins "complete", "compgen", and "compopt" in Bash.

Copyright (C) 1999-2009 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Bash.  If not, see <http://www.gnu.org/licenses/>.

$PRODUCES complete.c

$BUILTIN complete
$DEPENDS_ON PROGRAMMABLE_COMPLETION
$FUNCTION complete_builtin
$SHORT_DOC complete [-abcdefgjksuv] [-pr] [-o option] [-A action] [-G globpat] [-W wordlist]  [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...]
Specify how arguments are to be completed by Readline.

For each NAME, specify how arguments are to be completed.  If no options
are supplied, existing completion specifications are printed in a way that
allows them to be reused as input.

Options:
  -p	print existing completion specifications in a reusable format
  -r	remove a completion specification for each NAME, or, if no
	NAMEs are supplied, all completion specifications

When completion is attempted, the actions are applied in the order the
uppercase-letter options are listed above.

Exit Status:
Returns success unless an invalid option is supplied or an error occurs.
$END

#include <config.h>

#include <stdio.h>

#include "../bashtypes.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashansi.h"
#include "../bashintl.h"

#include "../shell.h"
#include "../builtins.h"
#include "../pcomplete.h"
#include "../bashline.h"

#include "common.h"
#include "bashgetopt.h"

#include <readline/readline.h>

#define STRDUP(x)       ((x) ? savestring (x) : (char *)NULL)

/* Structure containing all the non-action (binary) options; filled in by
   build_actions(). */
struct _optflags {
  int pflag;
  int rflag;
  int Eflag;
};

static int find_compact __P((char *));
static int find_compopt __P((char *));

static int build_actions __P((WORD_LIST *, struct _optflags *, unsigned long *, unsigned long *));

static int remove_cmd_completions __P((WORD_LIST *));

static int print_one_completion __P((char *, COMPSPEC *));
static int print_compitem __P((BUCKET_CONTENTS *));
static void print_compopts __P((const char *, COMPSPEC *, int));
static void print_all_completions __P((void));
static int print_cmd_completions __P((WORD_LIST *));

static char *Garg, *Warg, *Parg, *Sarg, *Xarg, *Farg, *Carg;

static const struct _compacts {
  const char * const actname;
  int actflag;
  int actopt;
} compacts[] = {
  { "alias",     CA_ALIAS,     'a' },
  { "arrayvar",  CA_ARRAYVAR,   0 },
  { "binding",   CA_BINDING,    0 },
  { "builtin",   CA_BUILTIN,   'b' },
  { "command",   CA_COMMAND,   'c' },
  { "directory", CA_DIRECTORY, 'd' },
  { "disabled",  CA_DISABLED,   0 },
  { "enabled",   CA_ENABLED,    0 },
  { "export",    CA_EXPORT,    'e' },
  { "file",      CA_FILE,      'f' },
  { "function",  CA_FUNCTION,   0 },
  { "helptopic", CA_BUILTIN,  0 },	/* for now */
  { "hostname",  CA_HOSTNAME,   0 },
  { "group",     CA_GROUP,     'g' },
  { "job",       CA_JOB,       'j' },
  { "keyword",   CA_KEYWORD,   'k' },
  { "running",   CA_RUNNING,    0 },
  { "service",   CA_SERVICE,   's' },
  { "setopt",    CA_SETOPT,     0 },
  { "shopt",     CA_SHOPT,      0 },
  { "signal",    CA_SIGNAL,     0 },
  { "stopped",   CA_STOPPED,    0 },
  { "user",      CA_USER,      'u' },
  { "variable",  CA_VARIABLE,  'v' },
  { (char *)NULL, 0, 0 },
};

/* This should be a STRING_INT_ALIST */
const static struct _compopt {
  const char * const optname;
  int optflag;
} compopts[] = {
  { "bashdefault", COPT_BASHDEFAULT },
  { "default",	COPT_DEFAULT },
  { "dirnames", COPT_DIRNAMES },
  { "filenames",COPT_FILENAMES},
  { "nospace",	COPT_NOSPACE },
  { "plusdirs", COPT_PLUSDIRS },
  { (char *)NULL, 0 },
};

static int
find_compact (name)
     char *name;
{
  register int i;

  for (i = 0; compacts[i].actname; i++)
    if (STREQ (name, compacts[i].actname))
      return i;
  return -1;
}

static int
find_compopt (name)
     char *name;
{
  register int i;

  for (i = 0; compopts[i].optname; i++)
    if (STREQ (name, compopts[i].optname))
      return i;
  return -1;
}

/* Build the actions and compspec options from the options specified in LIST.
   ACTP is a pointer to an unsigned long in which to place the bitmap of
   actions.  OPTP is a pointer to an unsigned long in which to place the
   btmap of compspec options (arguments to `-o').  PP, if non-null, gets 1
   if -p is supplied; RP, if non-null, gets 1 if -r is supplied.
   If either is null, the corresponding option generates an error.
   This also sets variables corresponding to options that take arguments as
   a side effect; the caller should ensure that those variables are set to
   NULL before calling build_actions.  Return value:
   	EX_USAGE = bad option
   	EXECUTION_SUCCESS = some options supplied
   	EXECUTION_FAILURE = no options supplied
*/

static int
build_actions (list, flagp, actp, optp)
     WORD_LIST *list;
     struct _optflags *flagp;
     unsigned long *actp, *optp;
{
  int opt, ind, opt_given;
  unsigned long acts, copts;

  acts = copts = (unsigned long)0L;
  opt_given = 0;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "abcdefgjko:prsuvA:G:W:P:S:X:F:C:E")) != -1)
    {
      opt_given = 1;
      switch (opt)
	{
	case 'r':
	  if (flagp)
	    {
	      flagp->rflag = 1;
	      break;
	    }
	  else
	    {
	      sh_invalidopt ("-r");
	      builtin_usage ();
	      return (EX_USAGE);
	    }

	case 'p':
	  if (flagp)
	    {
	      flagp->pflag = 1;
	      break;
	    }
	  else
	    {
	      sh_invalidopt ("-p");
	      builtin_usage ();
	      return (EX_USAGE);
	    }

	case 'a':
	  acts |= CA_ALIAS;
	  break;
	case 'b':
	  acts |= CA_BUILTIN;
	  break;
	case 'c':
	  acts |= CA_COMMAND;
	  break;
	case 'd':
	  acts |= CA_DIRECTORY;
	  break;
	case 'e':
	  acts |= CA_EXPORT;
	  break;
	case 'f':
	  acts |= CA_FILE;
	  break;
	case 'g':
	  acts |= CA_GROUP;
	  break;
	case 'j':
	  acts |= CA_JOB;
	  break;
	case 'k':
	  acts |= CA_KEYWORD;
	  break;
	case 's':
	  acts |= CA_SERVICE;
	  break;
	case 'u':
	  acts |= CA_USER;
	  break;
	case 'v':
	  acts |= CA_VARIABLE;
	  break;
	case 'o':
	  ind = find_compopt (list_optarg);
	  if (ind < 0)
	    {
	      sh_invalidoptname (list_optarg);
	      return (EX_USAGE);
	    }
	  copts |= compopts[ind].optflag;
	  break;
	case 'A':
	  ind = find_compact (list_optarg);
	  if (ind < 0)
	    {
	      builtin_error (_("%s: invalid action name"), list_optarg);
	      return (EX_USAGE);
	    }
	  acts |= compacts[ind].actflag;
	  break;
	case 'C':
	  Carg = list_optarg;
	  break;
	case 'E':
	  if (flagp)
	    {
	      flagp->Eflag = 1;
	      break;
	    }
	  else
	    {
	      sh_invalidopt ("-E");
	      builtin_usage ();
	      return (EX_USAGE);
	    }
	case 'F':
	  Farg = list_optarg;
	  break;
	case 'G':
	  Garg = list_optarg;
	  break;
	case 'P':
	  Parg = list_optarg;
	  break;
	case 'S':
	  Sarg = list_optarg;
	  break;
	case 'W':
	  Warg = list_optarg;
	  break;
	case 'X':
	  Xarg = list_optarg;
	  break;
	default:
	  builtin_usage ();
	  return (EX_USAGE);
	}
    }

  *actp = acts;
  *optp = copts;

  return (opt_given ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}

/* Add, remove, and display completion specifiers. */
int
complete_builtin (list)
     WORD_LIST *list;
{
  int opt_given, rval;
  unsigned long acts, copts;
  COMPSPEC *cs;
  struct _optflags oflags;
  WORD_LIST *l, *wl;

  if (list == 0)
    {
      print_all_completions ();
      return (EXECUTION_SUCCESS);
    }

  opt_given = oflags.pflag = oflags.rflag = oflags.Eflag = 0;

  acts = copts = (unsigned long)0L;
  Garg = Warg = Parg = Sarg = Xarg = Farg = Carg = (char *)NULL;
  cs = (COMPSPEC *)NULL;

  /* Build the actions from the arguments.  Also sets the [A-Z]arg variables
     as a side effect if they are supplied as options. */
  rval = build_actions (list, &oflags, &acts, &copts);
  if (rval == EX_USAGE)
    return (rval);
  opt_given = rval != EXECUTION_FAILURE;

  list = loptend;

  wl = oflags.Eflag ? make_word_list (make_bare_word ("_EmptycmD_"), (WORD_LIST *)NULL) : 0;

  /* -p overrides everything else */
  if (oflags.pflag || (list == 0 && opt_given == 0))
    {
      if (wl)
	{
	  rval = print_cmd_completions (wl);
	  dispose_words (wl);
	  return rval;
	}
      else if (list == 0)
	{
	  print_all_completions ();
	  return (EXECUTION_SUCCESS);
	}
      return (print_cmd_completions (list));
    }

  /* next, -r overrides everything else. */
  if (oflags.rflag)
    {
      if (wl)
	{
	  rval = remove_cmd_completions (wl);
	  dispose_words (wl);
	  return rval;
	}
      else if (list == 0)
	{
	  progcomp_flush ();
	  return (EXECUTION_SUCCESS);
	}
      return (remove_cmd_completions (list));
    }

  if (wl == 0 && list == 0 && opt_given)
    {
      builtin_usage ();
      return (EX_USAGE);
    }

  /* If we get here, we need to build a compspec and add it for each
     remaining argument. */
  cs = compspec_create ();
  cs->actions = acts;
  cs->options = copts;

  cs->globpat = STRDUP (Garg);
  cs->words = STRDUP (Warg);
  cs->prefix = STRDUP (Parg);
  cs->suffix = STRDUP (Sarg);
  cs->funcname = STRDUP (Farg);
  cs->command = STRDUP (Carg);
  cs->filterpat = STRDUP (Xarg);

  for (rval = EXECUTION_SUCCESS, l = wl ? wl : list ; l; l = l->next)
    {
      /* Add CS as the compspec for the specified commands. */
      if (progcomp_insert (l->word->word, cs) == 0)
	rval = EXECUTION_FAILURE;
    }

  dispose_words (wl);
  return (rval);
}

static int
remove_cmd_completions (list)
     WORD_LIST *list;
{
  WORD_LIST *l;
  int ret;

  for (ret = EXECUTION_SUCCESS, l = list; l; l = l->next)
    {
      if (progcomp_remove (l->word->word) == 0)
	{
	  builtin_error (_("%s: no completion specification"), l->word->word);
	  ret = EXECUTION_FAILURE;
	}
    }
  return ret;
}

#define SQPRINTARG(a, f) \
  do { \
    if (a) \
      { \
      	x = sh_single_quote (a); \
	printf ("%s %s ", f, x); \
	free (x); \
      } \
  } while (0)

#define PRINTARG(a, f) \
  do { \
    if (a) \
      printf ("%s %s ", f, a); \
  } while (0)

#define PRINTOPT(a, f) \
  do { \
    if (acts & a) \
      printf ("%s ", f); \
  } while (0)

#define PRINTACT(a, f) \
  do { \
    if (acts & a) \
      printf ("-A %s ", f); \
  } while (0)

#define PRINTCOMPOPT(a, f) \
  do { \
    if (copts & a) \
      printf ("-o %s ", f); \
  } while (0)

#define XPRINTCOMPOPT(a, f) \
  do { \
    if (copts & a) \
      printf ("-o %s ", f); \
    else \
      printf ("+o %s ", f); \
  } while (0)

static int
print_one_completion (cmd, cs)
     char *cmd;
     COMPSPEC *cs;
{
  unsigned long acts, copts;
  char *x;

  printf ("complete ");

  copts = cs->options;

  /* First, print the -o options. */
  PRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault");
  PRINTCOMPOPT (COPT_DEFAULT, "default");
  PRINTCOMPOPT (COPT_DIRNAMES, "dirnames");
  PRINTCOMPOPT (COPT_FILENAMES, "filenames");
  PRINTCOMPOPT (COPT_NOSPACE, "nospace");
  PRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs");

  acts = cs->actions;

  /* simple flags next */
  PRINTOPT (CA_ALIAS, "-a");
  PRINTOPT (CA_BUILTIN, "-b");
  PRINTOPT (CA_COMMAND, "-c");
  PRINTOPT (CA_DIRECTORY, "-d");
  PRINTOPT (CA_EXPORT, "-e");
  PRINTOPT (CA_FILE, "-f");
  PRINTOPT (CA_GROUP, "-g");
  PRINTOPT (CA_JOB, "-j");
  PRINTOPT (CA_KEYWORD, "-k");
  PRINTOPT (CA_SERVICE, "-s");
  PRINTOPT (CA_USER, "-u");
  PRINTOPT (CA_VARIABLE, "-v");

  /* now the rest of the actions */
  PRINTACT (CA_ARRAYVAR, "arrayvar");
  PRINTACT (CA_BINDING, "binding");
  PRINTACT (CA_DISABLED, "disabled");
  PRINTACT (CA_ENABLED, "enabled");
  PRINTACT (CA_FUNCTION, "function");
  PRINTACT (CA_HELPTOPIC, "helptopic");
  PRINTACT (CA_HOSTNAME, "hostname");
  PRINTACT (CA_RUNNING, "running");
  PRINTACT (CA_SETOPT, "setopt");
  PRINTACT (CA_SHOPT, "shopt");
  PRINTACT (CA_SIGNAL, "signal");
  PRINTACT (CA_STOPPED, "stopped");

  /* now the rest of the arguments */

  /* arguments that require quoting */
  SQPRINTARG (cs->globpat, "-G");
  SQPRINTARG (cs->words, "-W");
  SQPRINTARG (cs->prefix, "-P");
  SQPRINTARG (cs->suffix, "-S");
  SQPRINTARG (cs->filterpat, "-X");

  SQPRINTARG (cs->command, "-C");

  /* simple arguments that don't require quoting */
  PRINTARG (cs->funcname, "-F");

  printf ("%s\n", cmd);

  return (0);
}

static void
print_compopts (cmd, cs, full)
     const char *cmd;
     COMPSPEC *cs;
     int full;
{
  int copts;

  printf ("compopt ");
  copts = cs->options;

  if (full)
    {
      XPRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault");
      XPRINTCOMPOPT (COPT_DEFAULT, "default");
      XPRINTCOMPOPT (COPT_DIRNAMES, "dirnames");
      XPRINTCOMPOPT (COPT_FILENAMES, "filenames");
      XPRINTCOMPOPT (COPT_NOSPACE, "nospace");
      XPRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs");
    }
  else
    {
      PRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault");
      PRINTCOMPOPT (COPT_DEFAULT, "default");
      PRINTCOMPOPT (COPT_DIRNAMES, "dirnames");
      PRINTCOMPOPT (COPT_FILENAMES, "filenames");
      PRINTCOMPOPT (COPT_NOSPACE, "nospace");
      PRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs");
    }

  printf ("%s\n", cmd);
}

static int
print_compitem (item)
     BUCKET_CONTENTS *item;
{
  COMPSPEC *cs;
  char *cmd;

  cmd = item->key;
  cs = (COMPSPEC *)item->data;

  return (print_one_completion (cmd, cs));
}

static void
print_all_completions ()
{
  progcomp_walk (print_compitem);
}

static int
print_cmd_completions (list)
     WORD_LIST *list;
{
  WORD_LIST *l;
  COMPSPEC *cs;
  int ret;

  for (ret = EXECUTION_SUCCESS, l = list; l; l = l->next)
    {
      cs = progcomp_search (l->word->word);
      if (cs)
	print_one_completion (l->word->word, cs);
      else
	{
	  builtin_error (_("%s: no completion specification"), l->word->word);
	  ret = EXECUTION_FAILURE;
	}
    }

  return (sh_chkwrite (ret));
}

$BUILTIN compgen
$DEPENDS_ON PROGRAMMABLE_COMPLETION
$FUNCTION compgen_builtin
$SHORT_DOC compgen [-abcdefgjksuv] [-o option]  [-A action] [-G globpat] [-W wordlist]  [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [word]
Display possible completions depending on the options.

Intended to be used from within a shell function generating possible
completions.  If the optional WORD argument is supplied, matches against
WORD are generated.

Exit Status:
Returns success unless an invalid option is supplied or an error occurs.
$END

int
compgen_builtin (list)
     WORD_LIST *list;
{
  int rval;
  unsigned long acts, copts;
  COMPSPEC *cs;
  STRINGLIST *sl;
  char *word, **matches;

  if (list == 0)
    return (EXECUTION_SUCCESS);

  acts = copts = (unsigned long)0L;
  Garg = Warg = Parg = Sarg = Xarg = Farg = Carg = (char *)NULL;
  cs = (COMPSPEC *)NULL;

  /* Build the actions from the arguments.  Also sets the [A-Z]arg variables
     as a side effect if they are supplied as options. */
  rval = build_actions (list, (struct _optflags *)NULL, &acts, &copts);
  if (rval == EX_USAGE)
    return (rval);
  if (rval == EXECUTION_FAILURE)
    return (EXECUTION_SUCCESS);

  list = loptend;

  word = (list && list->word) ? list->word->word : "";

  if (Farg)
    builtin_error (_("warning: -F option may not work as you expect"));
  if (Carg)
    builtin_error (_("warning: -C option may not work as you expect"));

  /* If we get here, we need to build a compspec and evaluate it. */
  cs = compspec_create ();
  cs->actions = acts;
  cs->options = copts;
  cs->refcount = 1;

  cs->globpat = STRDUP (Garg);
  cs->words = STRDUP (Warg);
  cs->prefix = STRDUP (Parg);
  cs->suffix = STRDUP (Sarg);
  cs->funcname = STRDUP (Farg);
  cs->command = STRDUP (Carg);
  cs->filterpat = STRDUP (Xarg);

  rval = EXECUTION_FAILURE;
  sl = gen_compspec_completions (cs, "compgen", word, 0, 0);

  /* If the compspec wants the bash default completions, temporarily
     turn off programmable completion and call the bash completion code. */
  if ((sl == 0 || sl->list_len == 0) && (copts & COPT_BASHDEFAULT))
    {
      matches = bash_default_completion (word, 0, 0, 0, 0);
      sl = completions_to_stringlist (matches);
      strvec_dispose (matches);
    }

  /* This isn't perfect, but it's the best we can do, given what readline
     exports from its set of completion utility functions. */
  if ((sl == 0 || sl->list_len == 0) && (copts & COPT_DEFAULT))
    {
      matches = rl_completion_matches (word, rl_filename_completion_function);
      sl = completions_to_stringlist (matches);
      strvec_dispose (matches);
    }

  if (sl)
    {
      if (sl->list && sl->list_len)
	{
	  rval = EXECUTION_SUCCESS;
	  strlist_print (sl, (char *)NULL);
	}
      strlist_dispose (sl);
    }

  compspec_dispose (cs);
  return (rval);
}

$BUILTIN compopt
$DEPENDS_ON PROGRAMMABLE_COMPLETION
$FUNCTION compopt_builtin
$SHORT_DOC compopt [-o|+o option] [name ...]
Modify or display completion options.

Modify the completion options for each NAME, or, if no NAMEs are supplied,
the completion currently begin executed.  If no OPTIONs are givenm, print
the completion options for each NAME or the current completion specification.

Options:
	-o option	Set completion option OPTION for each NAME

Using `+o' instead of `-o' turns off the specified option.

Arguments:

Each NAME refers to a command for which a completion specification must
have previously been defined using the `complete' builtin.  If no NAMEs
are supplied, compopt must be called by a function currently generating
completions, and the options for that currently-executing completion
generator are modified.

Exit Status:
Returns success unless an invalid option is supplied or NAME does not
have a completion specification defined.
$END

int
compopt_builtin (list)
     WORD_LIST *list;
{
  int opts_on, opts_off, *opts, opt, oind, ret, Eflag;
  WORD_LIST *l;
  COMPSPEC *cs;

  opts_on = opts_off = 0;
  ret = EXECUTION_SUCCESS;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "+o:")) != EOF)
    {
      opts = (list_opttype == '-') ? &opts_on : &opts_off;

      switch (opt)
	{
	case 'o':
	  oind = find_compopt (list_optarg);
	  if (oind < 0)
	    {
	      sh_invalidoptname (list_optarg);
	      return (EX_USAGE);
	    }
	  *opts |= compopts[oind].optflag;
	  break;
	default:
	  builtin_usage ();
	  return (EX_USAGE);
	}
    }
  list = loptend;

  if (list == 0)
    {
      if (RL_ISSTATE (RL_STATE_COMPLETING) == 0 || pcomp_curcs == 0)
	{
	  builtin_error (_("not currently executing completion function"));
	  return (EXECUTION_FAILURE);
	}
      cs = pcomp_curcs;

      if (opts_on == 0 && opts_off == 0)
	{
	  print_compopts (pcomp_curcmd, cs, 1);
          return (sh_chkwrite (ret));
	}

      /* Set the compspec options */
      pcomp_set_compspec_options (cs, opts_on, 1);
      pcomp_set_compspec_options (cs, opts_off, 0);

      /* And change the readline variables the options control */
      pcomp_set_readline_variables (opts_on, 1);
      pcomp_set_readline_variables (opts_off, 0);

      return (ret);
    }

  for (l = list; l; l = l->next)
    {
      cs = progcomp_search (l->word->word);
      if (cs == 0)
	{
	  builtin_error (_("%s: no completion specification"), l->word->word);
	  ret = EXECUTION_FAILURE;
	  continue;
	}
      if (opts_on == 0 && opts_off == 0)
	{
	  print_compopts (l->word->word, cs, 1);
	  continue;			/* XXX -- fill in later */
	}

      /* Set the compspec options */
      pcomp_set_compspec_options (cs, opts_on, 1);
      pcomp_set_compspec_options (cs, opts_off, 0);
    }

  return (ret);
}
