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;
66 fprintf(fp, "%s) COMPREPLY=( $(compgen -S ' ' -d $c) );;\n",
70 fprintf(fp, "%s) COMPREPLY=( $(compgen -S ' ' -X '!*.", tfn[i].opt);
71 const int genericCount = ftp2generic_count(ftp);
75 const int *const genericTypes = ftp2generic_list(ftp);
76 for (int j = 0; j < genericCount; j++)
82 fprintf(fp, "%s", ftp2ext(genericTypes[j]));
88 fprintf(fp, "%s", ftp2ext(ftp));
91 for (int j = 0; j < NZEXT; j++)
97 fprintf(fp, "%s", z_ext[j]);
99 fprintf(fp, ")' -f $c ; compgen -S '/' -d $c ));;\n");
103 static void pr_opts(FILE *fp,
104 int nfile, t_filenm *fnm,
105 int npargs, t_pargs pa[])
107 fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen -S ' ' -W $'");
108 const char *sep = "";
109 for (int i = 0; i < nfile; i++)
111 fprintf(fp, "%s-%s", sep, fnm[i].opt+1);
114 for (int i = 0; i < npargs; i++)
116 if (pa[i].type == etBOOL && *(pa[i].u.b))
118 fprintf(fp, "%s-no%s", sep, pa[i].option + 1);
122 fprintf(fp, "%s-%s", sep, pa[i].option + 1);
126 fprintf(fp, "' -- $c)); return 0; fi\n");
129 static void pr_enums(FILE *fp, int npargs, t_pargs pa[])
131 for (int i = 0; i < npargs; i++)
133 if (pa[i].type == etENUM)
135 fprintf(fp, "%s) COMPREPLY=( $(compgen -S ' ' -W $'", pa[i].option);
136 for (int j = 1; pa[i].u.c[j]; j++)
138 fprintf(fp, "%s%s", (j == 1 ? "" : "\\n"), pa[i].u.c[j]);
140 fprintf(fp, "' -- $c ));;\n");
145 static void write_bashcompl(FILE *out,
146 const char *funcName,
147 int nfile, t_filenm *fnm,
148 int npargs, t_pargs *pa)
150 /* Advanced bash completions are handled by shell functions.
151 * p and c hold the previous and current word on the command line.
153 fprintf(out, "%s() {\n", funcName);
154 fprintf(out, "local p c\n");
155 fprintf(out, "local IFS=$'\\n'\n");
156 fprintf(out, "COMPREPLY=() c=${COMP_WORDS[COMP_CWORD]} p=${COMP_WORDS[COMP_CWORD-1]}\n");
157 pr_opts(out, nfile, fnm, npargs, pa);
158 fprintf(out, "case \"$p\" in\n");
160 pr_enums(out, npargs, pa);
161 pr_fopts(out, nfile, fnm);
162 fprintf(out, "esac }\n");
168 class ShellCompletionWriter::Impl
171 Impl(const std::string &binaryName, ShellCompletionFormat /*format*/)
172 : binaryName_(binaryName)
176 std::string completionFunctionName(const char *moduleName) const
178 // TODO: Consider if some characters need to be escaped.
179 return formatString("_%s_%s_compl", binaryName_.c_str(), moduleName);
182 std::string binaryName_;
183 boost::scoped_ptr<File> file_;
186 ShellCompletionWriter::ShellCompletionWriter(const std::string &binaryName,
187 ShellCompletionFormat format)
188 : impl_(new Impl(binaryName, format))
192 ShellCompletionWriter::~ShellCompletionWriter()
196 File *ShellCompletionWriter::outputFile()
198 return impl_->file_.get();
201 void ShellCompletionWriter::startCompletions()
203 impl_->file_.reset(new File(impl_->binaryName_ + "-completion.bash", "w"));
204 impl_->file_->writeLine("shopt -s extglob");
207 void ShellCompletionWriter::writeLegacyModuleCompletions(
208 const char *moduleName,
209 int nfile, t_filenm *fnm,
210 int npargs, t_pargs *pa)
215 // Remove hidden arguments.
218 for (int i = 0; i < npargs; i++)
220 if (!is_hidden(&pa[i]))
227 write_bashcompl(impl_->file_->handle(),
228 impl_->completionFunctionName(moduleName).c_str(),
229 nfile, fnm, npar, par);
234 void ShellCompletionWriter::writeModuleCompletions(
235 const char *moduleName,
236 const Options & /*options*/)
239 impl_->file_->writeLine(
240 impl_->completionFunctionName(moduleName) + "() {\nCOMPREPLY=()\n}\n");
243 void ShellCompletionWriter::writeWrapperCompletions(
244 const ModuleNameList &modules)
246 impl_->file_->writeLine("_" + impl_->binaryName_ + "_compl() {");
247 impl_->file_->writeLine("local i c m");
248 impl_->file_->writeLine("local IFS=$'\\n'\n");
249 impl_->file_->writeLine("COMPREPLY=()");
250 impl_->file_->writeLine("unset COMP_WORDS[0]");
251 impl_->file_->writeLine("for ((i=1;i<COMP_CWORD;++i)) ; do");
252 impl_->file_->writeLine("if [[ \"${COMP_WORDS[i]}\" != -* ]]; then break ; fi");
253 impl_->file_->writeLine("unset COMP_WORDS[i]");
254 impl_->file_->writeLine("done");
255 impl_->file_->writeLine("if (( i == COMP_CWORD )); then");
256 impl_->file_->writeLine("c=${COMP_WORDS[COMP_CWORD]}");
257 // TODO: Get rid of these hard-coded options.
258 std::string completions("-h\\n-quiet\\n-version\\n-nocopyright");
259 for (ModuleNameList::const_iterator i = modules.begin();
260 i != modules.end(); ++i)
262 completions.append("\\n");
263 completions.append(*i);
265 impl_->file_->writeLine("COMPREPLY=( $(compgen -S ' ' -W $'" + completions + "' -- $c) )");
266 impl_->file_->writeLine("return 0");
267 impl_->file_->writeLine("fi");
268 impl_->file_->writeLine("m=${COMP_WORDS[i]}");
269 impl_->file_->writeLine("COMP_WORDS=( \"${COMP_WORDS[@]}\" )");
270 impl_->file_->writeLine("COMP_CWORD=$((COMP_CWORD-i))");
271 impl_->file_->writeLine("case \"$m\" in");
272 for (ModuleNameList::const_iterator i = modules.begin();
273 i != modules.end(); ++i)
275 const char *const name = i->c_str();
276 impl_->file_->writeLine(formatString("%s) %s ;;", name,
277 impl_->completionFunctionName(name).c_str()));
279 impl_->file_->writeLine("esac }");
282 void ShellCompletionWriter::finishCompletions()
284 impl_->file_->close();
285 impl_->file_.reset();