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 const int ftp = tfn[i].ftp;
64 const char *multiplicity = "(( $n == 1 )) && ";
65 if (tfn[i].flag & ffMULT)
71 fprintf(fp, "%s) %sCOMPREPLY=( $(compgen -S ' ' -d $c) );;\n",
72 tfn[i].opt, multiplicity);
75 fprintf(fp, "%s) %sCOMPREPLY=( $(compgen -S ' ' -X '!*.",
76 tfn[i].opt, multiplicity);
77 const int genericCount = ftp2generic_count(ftp);
81 const int *const genericTypes = ftp2generic_list(ftp);
82 for (int j = 0; j < genericCount; j++)
88 fprintf(fp, "%s", ftp2ext(genericTypes[j]));
94 fprintf(fp, "%s", ftp2ext(ftp));
97 for (int j = 0; j < NZEXT; j++)
103 fprintf(fp, "%s", z_ext[j]);
105 fprintf(fp, ")' -f $c ; compgen -S '/' -d $c ));;\n");
109 static void pr_opts(FILE *fp,
110 int nfile, t_filenm *fnm,
111 int npargs, t_pargs pa[])
113 fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen -S ' ' -W $'");
114 const char *sep = "";
115 for (int i = 0; i < nfile; i++)
117 fprintf(fp, "%s-%s", sep, fnm[i].opt+1);
120 for (int i = 0; i < npargs; i++)
122 if (pa[i].type == etBOOL && *(pa[i].u.b))
124 fprintf(fp, "%s-no%s", sep, pa[i].option + 1);
128 fprintf(fp, "%s-%s", sep, pa[i].option + 1);
132 fprintf(fp, "' -- $c)); return 0; fi\n");
135 static void pr_enums(FILE *fp, int npargs, t_pargs pa[])
137 for (int i = 0; i < npargs; i++)
139 if (pa[i].type == etENUM)
141 fprintf(fp, "%s) (( $n == 1 )) && COMPREPLY=( $(compgen -S ' ' -W $'", pa[i].option);
142 for (int j = 1; pa[i].u.c[j]; j++)
144 fprintf(fp, "%s%s", (j == 1 ? "" : "\\n"), pa[i].u.c[j]);
146 fprintf(fp, "' -- $c ));;\n");
151 static void write_bashcompl(FILE *out,
152 const char *funcName,
153 int nfile, t_filenm *fnm,
154 int npargs, t_pargs *pa)
156 /* Advanced bash completions are handled by shell functions.
157 * p and c hold the previous and current word on the command line.
159 fprintf(out, "%s() {\n", funcName);
160 fprintf(out, "local IFS=$'\\n'\n");
161 fprintf(out, "local c=${COMP_WORDS[COMP_CWORD]}\n");
162 fprintf(out, "local n\n");
163 fprintf(out, "for ((n=1;n<COMP_CWORD;++n)) ; do [[ \"${COMP_WORDS[COMP_CWORD-n]}\" == -* ]] && break ; done\n");
164 fprintf(out, "local p=${COMP_WORDS[COMP_CWORD-n]}\n");
165 fprintf(out, "COMPREPLY=()\n");
167 pr_opts(out, nfile, fnm, npargs, pa);
168 fprintf(out, "case \"$p\" in\n");
170 pr_enums(out, npargs, pa);
171 pr_fopts(out, nfile, fnm);
172 fprintf(out, "esac }\n");
178 class ShellCompletionWriter::Impl
181 Impl(const std::string &binaryName, ShellCompletionFormat /*format*/)
182 : binaryName_(binaryName)
186 std::string completionFunctionName(const char *moduleName) const
188 // TODO: Consider if some characters need to be escaped.
189 return formatString("_%s_%s_compl", binaryName_.c_str(), moduleName);
192 std::string binaryName_;
193 boost::scoped_ptr<File> file_;
196 ShellCompletionWriter::ShellCompletionWriter(const std::string &binaryName,
197 ShellCompletionFormat format)
198 : impl_(new Impl(binaryName, format))
202 ShellCompletionWriter::~ShellCompletionWriter()
206 File *ShellCompletionWriter::outputFile()
208 return impl_->file_.get();
211 void ShellCompletionWriter::startCompletions()
213 impl_->file_.reset(new File(impl_->binaryName_ + "-completion.bash", "w"));
214 impl_->file_->writeLine("shopt -s extglob");
217 void ShellCompletionWriter::writeLegacyModuleCompletions(
218 const char *moduleName,
219 int nfile, t_filenm *fnm,
220 int npargs, t_pargs *pa)
225 // Remove hidden arguments.
228 for (int i = 0; i < npargs; i++)
230 if (!is_hidden(&pa[i]))
237 write_bashcompl(impl_->file_->handle(),
238 impl_->completionFunctionName(moduleName).c_str(),
239 nfile, fnm, npar, par);
244 void ShellCompletionWriter::writeModuleCompletions(
245 const char *moduleName,
246 const Options & /*options*/)
249 impl_->file_->writeLine(
250 impl_->completionFunctionName(moduleName) + "() {\nCOMPREPLY=()\n}\n");
253 void ShellCompletionWriter::writeWrapperCompletions(
254 const ModuleNameList &modules)
256 impl_->file_->writeLine("_" + impl_->binaryName_ + "_compl() {");
257 impl_->file_->writeLine("local i c m");
258 impl_->file_->writeLine("local IFS=$'\\n'\n");
259 impl_->file_->writeLine("COMPREPLY=()");
260 impl_->file_->writeLine("unset COMP_WORDS[0]");
261 impl_->file_->writeLine("for ((i=1;i<COMP_CWORD;++i)) ; do");
262 impl_->file_->writeLine("[[ \"${COMP_WORDS[i]}\" != -* ]] && break");
263 impl_->file_->writeLine("unset COMP_WORDS[i]");
264 impl_->file_->writeLine("done");
265 impl_->file_->writeLine("if (( i == COMP_CWORD )); then");
266 impl_->file_->writeLine("c=${COMP_WORDS[COMP_CWORD]}");
267 // TODO: Get rid of these hard-coded options.
268 std::string completions("-h\\n-quiet\\n-version\\n-nocopyright");
269 for (ModuleNameList::const_iterator i = modules.begin();
270 i != modules.end(); ++i)
272 completions.append("\\n");
273 completions.append(*i);
275 impl_->file_->writeLine("COMPREPLY=( $(compgen -S ' ' -W $'" + completions + "' -- $c) )");
276 impl_->file_->writeLine("return 0");
277 impl_->file_->writeLine("fi");
278 impl_->file_->writeLine("m=${COMP_WORDS[i]}");
279 impl_->file_->writeLine("COMP_WORDS=( \"${COMP_WORDS[@]}\" )");
280 impl_->file_->writeLine("COMP_CWORD=$((COMP_CWORD-i))");
281 impl_->file_->writeLine("case \"$m\" in");
282 for (ModuleNameList::const_iterator i = modules.begin();
283 i != modules.end(); ++i)
285 const char *const name = i->c_str();
286 impl_->file_->writeLine(formatString("%s) %s ;;", name,
287 impl_->completionFunctionName(name).c_str()));
289 impl_->file_->writeLine("esac }");
292 void ShellCompletionWriter::finishCompletions()
294 impl_->file_->close();
295 impl_->file_.reset();