2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
37 #include "gromacs/commandline/shellcompletions.h"
43 #include <boost/scoped_ptr.hpp>
45 #include "gromacs/commandline/cmdlinehelpcontext.h"
46 #include "gromacs/commandline/pargs.h"
47 #include "gromacs/fileio/filenm.h"
48 #include "gromacs/utility/exceptions.h"
49 #include "gromacs/utility/file.h"
50 #include "gromacs/utility/gmxassert.h"
51 #include "gromacs/utility/stringutil.h"
53 #include "gromacs/legacyheaders/smalloc.h"
55 // TODO: Don't duplicate this from filenm.c/futil.c.
57 static const char *z_ext[NZEXT] = { ".gz", ".Z" };
59 static void pr_fopts(FILE *fp, int nf, const t_filenm tfn[])
61 for (int i = 0; i < nf; i++)
63 fprintf(fp, "%s) COMPREPLY=( $(compgen -X '!*.", tfn[i].opt);
64 const int ftp = tfn[i].ftp;
65 const int genericCount = ftp2generic_count(ftp);
69 const int *const genericTypes = ftp2generic_list(ftp);
70 for (int j = 0; j < genericCount; j++)
76 fprintf(fp, "%s", ftp2ext(genericTypes[j]));
82 fprintf(fp, "%s", ftp2ext(ftp));
85 for (int j = 0; j < NZEXT; j++)
91 fprintf(fp, "%s", z_ext[j]);
93 fprintf(fp, ")' -f $c ; compgen -S '/' -X '.*' -d $c ));;\n");
97 static void pr_opts(FILE *fp,
98 int nfile, t_filenm *fnm,
99 int npargs, t_pargs pa[])
101 fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen -W '");
102 for (int i = 0; i < nfile; i++)
104 fprintf(fp, " -%s", fnm[i].opt+1);
106 for (int i = 0; i < npargs; i++)
108 if (pa[i].type == etBOOL && *(pa[i].u.b))
110 fprintf(fp, " -no%s", pa[i].option+1);
114 fprintf(fp, " -%s", pa[i].option+1);
117 fprintf(fp, "' -- $c)); return 0; fi\n");
120 static void pr_enums(FILE *fp, int npargs, t_pargs pa[])
122 for (int i = 0; i < npargs; i++)
124 if (pa[i].type == etENUM)
126 fprintf(fp, "%s) COMPREPLY=( $(compgen -W '", pa[i].option);
127 for (int j = 1; pa[i].u.c[j]; j++)
129 fprintf(fp, " %s", pa[i].u.c[j]);
131 fprintf(fp, " ' -- $c ));;\n");
136 static void write_bashcompl(FILE *out,
137 const char *funcName,
138 int nfile, t_filenm *fnm,
139 int npargs, t_pargs *pa)
141 /* Advanced bash completions are handled by shell functions.
142 * p and c hold the previous and current word on the command line.
144 fprintf(out, "%s() {\n", funcName);
145 fprintf(out, "local p c\n");
146 fprintf(out, "COMPREPLY=() c=${COMP_WORDS[COMP_CWORD]} p=${COMP_WORDS[COMP_CWORD-1]}\n");
147 pr_opts(out, nfile, fnm, npargs, pa);
148 fprintf(out, "case \"$p\" in\n");
150 pr_enums(out, npargs, pa);
151 pr_fopts(out, nfile, fnm);
152 fprintf(out, "esac }\n");
158 class ShellCompletionWriter::Impl
161 Impl(const std::string &binaryName, ShellCompletionFormat /*format*/)
162 : binaryName_(binaryName)
166 std::string completionFunctionName(const char *moduleName) const
168 // TODO: Consider if some characters need to be escaped.
169 return formatString("_%s_%s_compl", binaryName_.c_str(), moduleName);
172 std::string binaryName_;
173 boost::scoped_ptr<File> file_;
176 ShellCompletionWriter::ShellCompletionWriter(const std::string &binaryName,
177 ShellCompletionFormat format)
178 : impl_(new Impl(binaryName, format))
182 ShellCompletionWriter::~ShellCompletionWriter()
186 File *ShellCompletionWriter::outputFile()
188 return impl_->file_.get();
191 void ShellCompletionWriter::startCompletions()
193 impl_->file_.reset(new File(impl_->binaryName_ + "-completion.bash", "w"));
194 impl_->file_->writeLine("shopt -s extglob");
197 void ShellCompletionWriter::writeLegacyModuleCompletions(
198 const char *moduleName,
199 int nfile, t_filenm *fnm,
200 int npargs, t_pargs *pa)
205 // Remove hidden arguments.
208 for (int i = 0; i < npargs; i++)
210 if (!is_hidden(&pa[i]))
217 write_bashcompl(impl_->file_->handle(),
218 impl_->completionFunctionName(moduleName).c_str(),
219 nfile, fnm, npar, par);
224 void ShellCompletionWriter::writeModuleCompletions(
225 const char *moduleName,
226 const Options & /*options*/)
229 impl_->file_->writeLine(
230 impl_->completionFunctionName(moduleName) + "() {\nCOMPREPLY=()\n}\n");
233 void ShellCompletionWriter::writeWrapperCompletions(
234 const ModuleNameList &modules)
236 impl_->file_->writeLine("_" + impl_->binaryName_ + "_compl() {");
237 impl_->file_->writeLine("local i c m");
238 impl_->file_->writeLine("COMPREPLY=()");
239 impl_->file_->writeLine("unset COMP_WORDS[0]");
240 impl_->file_->writeLine("for ((i=1;i<COMP_CWORD;++i)) ; do");
241 impl_->file_->writeLine("if [[ \"${COMP_WORDS[i]}\" != -* ]]; then break ; fi");
242 impl_->file_->writeLine("unset COMP_WORDS[i]");
243 impl_->file_->writeLine("done");
244 impl_->file_->writeLine("if (( i == COMP_CWORD )); then");
245 impl_->file_->writeLine("c=${COMP_WORDS[COMP_CWORD]}");
246 // TODO: Get rid of these hard-coded options.
247 std::string completions("-h -quiet -version -nocopyright");
248 for (ModuleNameList::const_iterator i = modules.begin();
249 i != modules.end(); ++i)
251 completions.append(" ");
252 completions.append(*i);
254 impl_->file_->writeLine("COMPREPLY=( $(compgen -W '" + completions + "' -- $c) )");
255 impl_->file_->writeLine("return 0");
256 impl_->file_->writeLine("fi");
257 impl_->file_->writeLine("m=${COMP_WORDS[i]}");
258 impl_->file_->writeLine("COMP_WORDS=( \"${COMP_WORDS[@]}\" )");
259 impl_->file_->writeLine("COMP_CWORD=$((COMP_CWORD-i))");
260 impl_->file_->writeLine("case \"$m\" in");
261 for (ModuleNameList::const_iterator i = modules.begin();
262 i != modules.end(); ++i)
264 const char *const name = i->c_str();
265 impl_->file_->writeLine(formatString("%s) %s ;;", name,
266 impl_->completionFunctionName(name).c_str()));
268 impl_->file_->writeLine("esac }");
271 void ShellCompletionWriter::finishCompletions()
273 impl_->file_->close();
274 impl_->file_.reset();