This file is complete.def, from which is created complete.c. It implements the builtins "complete", "compgen", and "compopt" in Bash. Copyright (C) 1999-2010 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 . $PRODUCES complete.c $BUILTIN complete $DEPENDS_ON PROGRAMMABLE_COMPLETION $FUNCTION complete_builtin $SHORT_DOC complete [-abcdefgjksuv] [-pr] [-DE] [-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 -D apply the completions and actions as the default for commands without any specific completion defined -E apply the completions and actions to "empty" commands -- completion attempted on a blank line When completion is attempted, the actions are applied in the order the uppercase-letter options are listed above. The -D option takes precedence over -E. Exit Status: Returns success unless an invalid option is supplied or an error occurs. $END #include #include #include "../bashtypes.h" #if defined (HAVE_UNISTD_H) # include #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 #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 Dflag; 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:DE")) != -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 'D': if (flagp) { flagp->Dflag = 1; break; } else { sh_invalidopt ("-D"); builtin_usage (); return (EX_USAGE); } 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.Dflag = 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.Dflag ? make_word_list (make_bare_word (DEFAULTCMD), (WORD_LIST *)NULL) : (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"); if (STREQ (cmd, EMPTYCMD)) printf ("-E\n"); else if (STREQ (cmd, DEFAULTCMD)) printf ("-D\n"); else 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"); } if (STREQ (cmd, EMPTYCMD)) printf ("-E\n"); else if (STREQ (cmd, DEFAULTCMD)) printf ("-D\n"); else 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, 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] [-DE] [name ...] Modify or display completion options. Modify the completion options for each NAME, or, if no NAMEs are supplied, the completion currently being executed. If no OPTIONs are given, print the completion options for each NAME or the current completion specification. Options: -o option Set completion option OPTION for each NAME -D Change options for the "default" command completion -E Change options for the "empty" command completion 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, Dflag, Eflag; WORD_LIST *l, *wl; COMPSPEC *cs; opts_on = opts_off = Eflag = Dflag = 0; ret = EXECUTION_SUCCESS; reset_internal_getopt (); while ((opt = internal_getopt (list, "+o:DE")) != 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; case 'D': Dflag = 1; break; case 'E': Eflag = 1; break; default: builtin_usage (); return (EX_USAGE); } } list = loptend; wl = Dflag ? make_word_list (make_bare_word (DEFAULTCMD), (WORD_LIST *)NULL) : (Eflag ? make_word_list (make_bare_word (EMPTYCMD), (WORD_LIST *)NULL) : 0); if (list == 0 && wl == 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 = wl ? wl : 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); }