Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / commandline / shellcompletions.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013, 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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 #include "gromacs/commandline/shellcompletions.h"
38
39 #include <cstdio>
40 #include <cstring>
41
42 #include "gromacs/commandline/pargs.h"
43 #include "gromacs/fileio/filenm.h"
44 #include "gromacs/fileio/gmxfio.h"
45
46 #include "gromacs/legacyheaders/copyrite.h"
47 #include "gromacs/legacyheaders/smalloc.h"
48
49 // Shell types for completion.
50 enum {
51     eshellCSH, eshellBASH, eshellZSH
52 };
53
54 // TODO: Don't duplicate this from filenm.c/futil.c.
55 #define NZEXT 2
56 static const char *z_ext[NZEXT] = { ".gz", ".Z" };
57
58 static void pr_fopts(FILE *fp, int nf, const t_filenm tfn[], int shell)
59 {
60     switch (shell)
61     {
62         case eshellCSH:
63             for (int i = 0; i < nf; i++)
64             {
65                 fprintf(fp, " \"n/%s/f:*.", tfn[i].opt);
66                 const int ftp          = tfn[i].ftp;
67                 const int genericCount = ftp2generic_count(ftp);
68                 if (genericCount > 0)
69                 {
70                     fprintf(fp, "{");
71                     const int *const genericTypes = ftp2generic_list(ftp);
72                     for (int j = 0; j < genericCount; j++)
73                     {
74                         if (j > 0)
75                         {
76                             fprintf(fp, ",");
77                         }
78                         fprintf(fp, "%s", ftp2ext(genericTypes[j]));
79                     }
80                     fprintf(fp, "}");
81                 }
82                 else
83                 {
84                     fprintf(fp, "%s", ftp2ext(ftp));
85                 }
86                 fprintf(fp, "{");
87                 for (int j = 0; j < NZEXT; j++)
88                 {
89                     fprintf(fp, ",%s", z_ext[j]);
90                 }
91                 fprintf(fp, "}/\"");
92             }
93             break;
94         case eshellBASH:
95             for (int i = 0; i < nf; i++)
96             {
97                 fprintf(fp, "%s) COMPREPLY=( $(compgen -X '!*.", tfn[i].opt);
98                 const int ftp          = tfn[i].ftp;
99                 const int genericCount = ftp2generic_count(ftp);
100                 if (genericCount > 0)
101                 {
102                     fprintf(fp, "+(");
103                     const int *const genericTypes = ftp2generic_list(ftp);
104                     for (int j = 0; j < genericCount; j++)
105                     {
106                         if (j > 0)
107                         {
108                             fprintf(fp, "|");
109                         }
110                         fprintf(fp, "%s", ftp2ext(genericTypes[j]));
111                     }
112                     fprintf(fp, ")");
113                 }
114                 else
115                 {
116                     fprintf(fp, "%s", ftp2ext(ftp));
117                 }
118                 fprintf(fp, "*(");
119                 for (int j = 0; j < NZEXT; j++)
120                 {
121                     if (j > 0)
122                     {
123                         fprintf(fp, "|");
124                     }
125                     fprintf(fp, "%s", z_ext[j]);
126                 }
127                 fprintf(fp, ")' -f $c ; compgen -S '/' -X '.*' -d $c ));;\n");
128             }
129             break;
130         case eshellZSH:
131             for (int i = 0; i < nf; i++)
132             {
133                 fprintf(fp, "- 'c[-1,%s]' -g '*.", tfn[i].opt);
134                 const int ftp          = tfn[i].ftp;
135                 const int genericCount = ftp2generic_count(ftp);
136                 if (genericCount > 0)
137                 {
138                     fprintf(fp, "(");
139                     const int *const genericTypes = ftp2generic_list(ftp);
140                     for (int j = 0; j < genericCount; j++)
141                     {
142                         if (j > 0)
143                         {
144                             fprintf(fp, "|");
145                         }
146                         fprintf(fp, "%s", ftp2ext(genericTypes[j]));
147                     }
148                     fprintf(fp, ")");
149                 }
150                 else
151                 {
152                     fprintf(fp, "%s", ftp2ext(ftp));
153                 }
154                 fprintf(fp, "(");
155                 for (int j = 0; j < NZEXT; j++)
156                 {
157                     fprintf(fp, "|%s", z_ext[j]);
158                 }
159                 fprintf(fp, ") *(/)' ");
160             }
161             break;
162     }
163 }
164
165 static void pr_opts(FILE *fp,
166                     int nfile,  t_filenm *fnm,
167                     int npargs, t_pargs pa[], int shell)
168 {
169     switch (shell)
170     {
171         case eshellCSH:
172             fprintf(fp, " \"c/-/(");
173             for (int i = 0; i < nfile; i++)
174             {
175                 fprintf(fp, " %s", fnm[i].opt+1);
176             }
177             for (int i = 0; i < npargs; i++)
178             {
179                 if (pa[i].type == etBOOL && *(pa[i].u.b))
180                 {
181                     fprintf(fp, " no%s", pa[i].option+1);
182                 }
183                 else
184                 {
185                     fprintf(fp, " %s", pa[i].option+1);
186                 }
187             }
188             fprintf(fp, ")/\"");
189             break;
190         case eshellBASH:
191             fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen  -W '");
192             for (int i = 0; i < nfile; i++)
193             {
194                 fprintf(fp, " -%s", fnm[i].opt+1);
195             }
196             for (int i = 0; i < npargs; i++)
197             {
198                 if (pa[i].type == etBOOL && *(pa[i].u.b))
199                 {
200                     fprintf(fp, " -no%s", pa[i].option+1);
201                 }
202                 else
203                 {
204                     fprintf(fp, " -%s", pa[i].option+1);
205                 }
206             }
207             fprintf(fp, "' -- $c)); return 0; fi\n");
208             break;
209         case eshellZSH:
210             fprintf(fp, " -x 's[-]' -s \"");
211             for (int i = 0; i < nfile; i++)
212             {
213                 fprintf(fp, " %s", fnm[i].opt+1);
214             }
215             for (int i = 0; i < npargs; i++)
216             {
217                 if (pa[i].type == etBOOL && *(pa[i].u.b))
218                 {
219                     fprintf(fp, " no%s", pa[i].option+1);
220                 }
221                 else
222                 {
223                     fprintf(fp, " %s", pa[i].option+1);
224                 }
225             }
226             fprintf(fp, "\" ");
227             break;
228     }
229 }
230
231 static void pr_enums(FILE *fp, int npargs, t_pargs pa[], int shell)
232 {
233     int i, j;
234
235     switch (shell)
236     {
237         case eshellCSH:
238             for (i = 0; i < npargs; i++)
239             {
240                 if (pa[i].type == etENUM)
241                 {
242                     fprintf(fp, " \"n/%s/(", pa[i].option);
243                     for (j = 1; pa[i].u.c[j]; j++)
244                     {
245                         fprintf(fp, " %s", pa[i].u.c[j]);
246                     }
247                     fprintf(fp, ")/\"");
248                 }
249             }
250             break;
251         case eshellBASH:
252             for (i = 0; i < npargs; i++)
253             {
254                 if (pa[i].type == etENUM)
255                 {
256                     fprintf(fp, "%s) COMPREPLY=( $(compgen -W '", pa[i].option);
257                     for (j = 1; pa[i].u.c[j]; j++)
258                     {
259                         fprintf(fp, " %s", pa[i].u.c[j]);
260                     }
261                     fprintf(fp, " ' -- $c ));;\n");
262                 }
263             }
264             break;
265         case eshellZSH:
266             for (i = 0; i < npargs; i++)
267             {
268                 if (pa[i].type == etENUM)
269                 {
270                     fprintf(fp, "- 'c[-1,%s]' -s \"", pa[i].option);
271                     for (j = 1; pa[i].u.c[j]; j++)
272                     {
273                         fprintf(fp, " %s", pa[i].u.c[j]);
274                     }
275                     fprintf(fp, "\" ");
276                 }
277             }
278             break;
279     }
280 }
281
282 static void write_bashcompl(FILE *out,
283                             int nfile,  t_filenm *fnm,
284                             int npargs, t_pargs *pa)
285 {
286     /* Advanced bash completions are handled by shell functions.
287      * p and c hold the previous and current word on the command line.
288      * We need to use extended globbing, so write it in each completion file */
289     fprintf(out, "shopt -s extglob\n");
290     fprintf(out, "_%s_compl() {\nlocal p c\n", ShortProgram());
291     fprintf(out, "COMPREPLY=() c=${COMP_WORDS[COMP_CWORD]} p=${COMP_WORDS[COMP_CWORD-1]}\n");
292     pr_opts(out, nfile, fnm, npargs, pa, eshellBASH);
293     fprintf(out, "case \"$p\" in\n");
294
295     pr_enums(out, npargs, pa, eshellBASH);
296     pr_fopts(out, nfile, fnm, eshellBASH);
297     fprintf(out, "esac }\ncomplete -F _%s_compl %s\n", ShortProgram(), ShortProgram());
298 }
299
300 static void write_cshcompl(FILE *out,
301                            int nfile,  t_filenm *fnm,
302                            int npargs, t_pargs *pa)
303 {
304     fprintf(out, "complete %s", ShortProgram());
305     pr_enums(out, npargs, pa, eshellCSH);
306     pr_fopts(out, nfile, fnm, eshellCSH);
307     pr_opts(out, nfile, fnm, npargs, pa, eshellCSH);
308     fprintf(out, "\n");
309 }
310
311 static void write_zshcompl(FILE *out,
312                            int nfile,  t_filenm *fnm,
313                            int npargs, t_pargs *pa)
314 {
315     fprintf(out, "compctl ");
316
317     /* start with options, since they are always present */
318     pr_opts(out, nfile, fnm, npargs, pa, eshellZSH);
319     pr_enums(out, npargs, pa, eshellZSH);
320     pr_fopts(out, nfile, fnm, eshellZSH);
321     fprintf(out, "-- %s\n", ShortProgram());
322 }
323
324 void write_completions(const char *type, const char *program,
325                        int nfile,  t_filenm *fnm,
326                        int npargs, t_pargs *pa)
327 {
328     char     buf[256];
329     sprintf(buf, "%s.%s", program, type);
330     FILE    *out = gmx_fio_fopen(buf, "w");
331
332     int      npar;
333     t_pargs *par;
334
335     // Remove hidden arguments.
336     snew(par, npargs);
337     npar = 0;
338     for (int i = 0; i < npargs; i++)
339     {
340         if (!is_hidden(&pa[i]))
341         {
342             par[npar] = pa[i];
343             npar++;
344         }
345     }
346
347     if (strcmp(type, "completion-zsh") == 0)
348     {
349         write_zshcompl(out, nfile, fnm, npar, par);
350     }
351     if (strcmp(type, "completion-bash") == 0)
352     {
353         write_bashcompl(out, nfile, fnm, npar, par);
354     }
355     if (strcmp(type, "completion-csh") == 0)
356     {
357         write_cshcompl(out, nfile, fnm, npar, par);
358     }
359
360     sfree(par);
361
362     gmx_fio_fclose(out);
363 }