ffd0e59576cbba0daf99b91f97c4345d0cf55b90
[alexxy/gromacs.git] / src / gromacs / commandline / wman.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) 2012,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/wman.h"
38
39 #include <cstdio>
40 #include <cstring>
41
42 #include <string>
43
44 #include "gromacs/commandline/cmdlinehelpcontext.h"
45 #include "gromacs/fileio/filenm.h"
46 #include "gromacs/fileio/gmxfio.h"
47 #include "gromacs/utility/file.h"
48 #include "gromacs/utility/gmxassert.h"
49 #include "gromacs/utility/stringutil.h"
50
51 #include "copyrite.h"
52 #include "gmx_fatal.h"
53 #include "string2.h"
54 #include "smalloc.h"
55
56 // Shell types, for completion stuff
57 enum {
58     eshellCSH, eshellBASH, eshellZSH
59 };
60
61 // TODO: Don't duplicate this from filenm.c/futil.c.
62 #define NZEXT 2
63 static const char *z_ext[NZEXT] = { ".gz", ".Z" };
64
65 /* The source code in this file should be thread-safe.
66  * Please keep it that way. */
67
68 static std::string check(const char *s, const gmx::HelpWriterContext &context)
69 {
70     return context.substituteMarkupAndWrapToString(gmx::TextLineWrapperSettings(), s);
71 }
72
73 static std::string check(const char *s, const gmx::CommandLineHelpContext &context)
74 {
75     return check(s, context.writerContext());
76 }
77
78 #define FLAG_SET(flag, mask) ((flag &mask) == mask)
79 /* Return a string describing the file type in flag.
80  * flag should the flag field of a filenm struct.
81  * You have to provide a buffer and buffer length in which
82  * the result will be written. The returned pointer is just
83  * a pointer to this buffer.
84  */
85 static char *fileopt(unsigned long flag, char buf[])
86 {
87     char tmp[256];
88
89     if (FLAG_SET(flag, ffRW))
90     {
91         sprintf(tmp, "In/Out");
92     }
93     else if (FLAG_SET(flag, ffREAD))
94     {
95         sprintf(tmp, "Input");
96     }
97     else if (FLAG_SET(flag, ffWRITE))
98     {
99         sprintf(tmp, "Output");
100     }
101     else
102     {
103         sprintf(tmp, "Dunno");
104     }
105
106     if (FLAG_SET(flag, ffOPT))
107     {
108         strcat(tmp, ", Opt");
109         if (FLAG_SET(flag, ffSET))
110         {
111             strcat(tmp, "!");
112         }
113         else
114         {
115             strcat(tmp, ".");
116         }
117     }
118     if (FLAG_SET(flag, ffLIB))
119     {
120         strcat(tmp, ", Lib.");
121     }
122     if (FLAG_SET(flag, ffMULT))
123     {
124         strcat(tmp, ", Mult.");
125     }
126
127     sprintf(buf, "%s", tmp);
128
129     return buf;
130 }
131
132 #define OPTLEN 4
133 #define NAMELEN 14
134 static void pr_fns(FILE *fp, int nf, const t_filenm tfn[])
135 {
136     int    i, f;
137     size_t j;
138     char   buf[256], *wbuf, opt_buf[32];
139
140     fprintf(fp, "%6s %12s  %-12s %s\n", "Option", "Filename", "Type",
141             "Description");
142     fprintf(fp,
143             "------------------------------------------------------------\n");
144     for (i = 0; (i < nf); i++)
145     {
146         for (f = 0; (f < tfn[i].nfiles); f++)
147         {
148             sprintf(buf, "%4s %14s  %-12s ", (f == 0) ? tfn[i].opt : "",
149                     tfn[i].fns[f], (f == 0) ? fileopt(tfn[i].flag, opt_buf)
150                     : "");
151             if (f < tfn[i].nfiles - 1)
152             {
153                 fprintf(fp, "%s\n", buf);
154             }
155         }
156         if (tfn[i].nfiles > 0)
157         {
158             strcat(buf, ftp2desc(tfn[i].ftp));
159             if ((strlen(tfn[i].opt) > OPTLEN)
160                 && (strlen(tfn[i].opt) <= ((OPTLEN + NAMELEN)
161                                            - strlen(tfn[i].fns[tfn[i].nfiles - 1]))))
162             {
163                 for (j = strlen(tfn[i].opt); j < strlen(buf)
164                      - (strlen(tfn[i].opt) - OPTLEN) + 1; j++)
165                 {
166                     buf[j] = buf[j + strlen(tfn[i].opt) - OPTLEN];
167                 }
168             }
169             wbuf = wrap_lines(buf, 78, 35, FALSE);
170             fprintf(fp, "%s\n", wbuf);
171             sfree(wbuf);
172         }
173     }
174     fprintf(fp, "\n");
175     fflush(fp);
176 }
177 #undef OPTLEN
178 #undef NAMELEN
179
180 /* name to print in help info for command line arguments
181  * (defined in enum in readinp.h) */
182 static const char *get_arg_desc(int type)
183 {
184     const char *const argtp[etNR] = {
185         "int", "step", "real", "time", "string", "bool", "vector", "enum"
186     };
187     return argtp[type];
188 }
189
190 /* Return the value of pa in the provided buffer buf, of size sz.
191  * The return value is also a pointer to buf.
192  */
193 static char *pa_val(t_pargs *pa, char buf[], int sz)
194 {
195     real r;
196     char buf_str[1256]; buf_str[0] = '\0';
197
198     buf[0] = '\0';
199
200     GMX_RELEASE_ASSERT(sz >= 255, "Buffer must be at least 255 chars");
201
202     switch (pa->type)
203     {
204         case etINT:
205             sprintf(buf, "%-d", *(pa->u.i));
206             break;
207         case etGMX_LARGE_INT:
208             sprintf(buf, gmx_large_int_pfmt, *(pa->u.is));
209             break;
210         case etTIME:
211         case etREAL:
212             r = *(pa->u.r);
213             sprintf(buf_str, "%-6g", r);
214             strcpy(buf, buf_str);
215             break;
216         case etBOOL:
217             sprintf(buf, "%-6s", *(pa->u.b) ? "yes" : "no");
218             break;
219         case etSTR:
220             if (*(pa->u.c))
221             {
222                 if (strlen(*(pa->u.c)) >= (size_t)sz)
223                 {
224                     gmx_fatal(FARGS, "Argument too long: \"%d\"\n", *(pa->u.c));
225                 }
226                 else
227                 {
228                     strcpy(buf, *(pa->u.c));
229                 }
230             }
231             break;
232         case etENUM:
233             strcpy(buf, *(pa->u.c));
234             break;
235         case etRVEC:
236             sprintf(buf, "%g %g %g", (*pa->u.rv)[0],
237                     (*pa->u.rv)[1],
238                     (*pa->u.rv)[2]);
239             break;
240     }
241     return buf;
242 }
243
244 #define OPTLEN 12
245 #define TYPELEN 6
246 #define LONGSTR 1024
247 static char *pargs_print_line(t_pargs *pa, const gmx::HelpWriterContext &context)
248 {
249     char buf[LONGSTR], *buf2, *tmp;
250
251     snew(buf2, LONGSTR+strlen(pa->desc));
252     snew(tmp, LONGSTR+strlen(pa->desc));
253
254     if (pa->type == etBOOL)
255     {
256         sprintf(buf, "-[no]%s", pa->option+1);
257     }
258     else
259     {
260         strcpy(buf, pa->option);
261     }
262     std::string desc = check(pa->desc, context);
263     if ((int)strlen(buf) > ((OPTLEN+TYPELEN)-std::max((int)strlen(get_arg_desc(pa->type)), 4)))
264     {
265         sprintf(buf2, "%s %-6s %-6s  %-s\n",
266                 buf, get_arg_desc(pa->type), pa_val(pa, tmp, LONGSTR-1),
267                 desc.c_str());
268     }
269     else if (strlen(buf) > OPTLEN)
270     {
271         /* so type can be 3 or 4 char's, this fits in the %4s */
272         sprintf(buf2, "%-14s %-4s %-6s  %-s\n",
273                 buf, get_arg_desc(pa->type), pa_val(pa, tmp, LONGSTR-1),
274                 desc.c_str());
275     }
276     else
277     {
278         sprintf(buf2, "%-12s %-6s %-6s  %-s\n",
279                 buf, get_arg_desc(pa->type), pa_val(pa, tmp, LONGSTR-1),
280                 desc.c_str());
281     }
282     sfree(tmp);
283
284     tmp = wrap_lines(buf2, 78, 28, FALSE);
285
286     sfree(buf2);
287
288     return tmp;
289 }
290 #undef OPTLEN
291 #undef TYPELEN
292 #undef LONGSTR
293
294 static void print_pargs(FILE *fp, int npargs, t_pargs pa[],
295                         const gmx::HelpWriterContext &context)
296 {
297     if (npargs > 0)
298     {
299         fprintf(fp, "%-12s %-6s %-6s  %-s\n",
300                 "Option", "Type", "Value", "Description");
301         fprintf(fp, "------------------------------------------------------\n");
302         for (int i = 0; i < npargs; i++)
303         {
304             char *wdesc = pargs_print_line(&pa[i], context);
305             fprintf(fp, "%s", wdesc);
306             sfree(wdesc);
307         }
308         fprintf(fp, "\n");
309     }
310 }
311
312 static void write_nroffman(FILE *out,
313                            int nldesc, const char **desc,
314                            int nfile, t_filenm *fnm,
315                            int npargs, t_pargs *pa,
316                            int nbug, const char **bugs,
317                            const gmx::CommandLineHelpContext &context)
318 {
319     int  i;
320     char tmp[256];
321
322     fprintf(out, ".SH SYNOPSIS\n");
323     fprintf(out, "\\f3%s\\fP\n", context.moduleDisplayName());
324
325     /* command line arguments */
326     if (nfile > 0)
327     {
328         for (i = 0; (i < nfile); i++)
329         {
330             fprintf(out, ".BI \"%s\" \" %s \"\n",
331                     check(fnm[i].opt, context).c_str(),
332                     check(fnm[i].fns[0], context).c_str());
333         }
334     }
335     if (npargs > 0)
336     {
337         for (i = 0; (i < npargs); i++)
338         {
339             if (pa[i].type == etBOOL)
340             {
341                 fprintf(out, ".BI \"\\-[no]%s\" \"\"\n",
342                         check(pa[i].option+1, context).c_str());
343             }
344             else
345             {
346                 fprintf(out, ".BI \"%s\" \" %s \"\n",
347                         check(pa[i].option, context).c_str(),
348                         check(get_arg_desc(pa[i].type), context).c_str());
349             }
350         }
351     }
352
353     /* description */
354     if (nldesc > 0)
355     {
356         fprintf(out, ".SH DESCRIPTION\n");
357         for (i = 0; (i < nldesc); i++)
358         {
359             fprintf(out, "\\&%s\n", check(desc[i], context).c_str());
360         }
361     }
362
363     /* FILES */
364     if (nfile > 0)
365     {
366         fprintf(out, ".SH FILES\n");
367         for (i = 0; (i < nfile); i++)
368         {
369             fprintf(out, ".BI \"%s\" \" %s\" \n.B %s\n %s \n\n",
370                     check(fnm[i].opt, context).c_str(),
371                     check(fnm[i].fns[0], context).c_str(),
372                     check(fileopt(fnm[i].flag, tmp), context).c_str(),
373                     check(ftp2desc(fnm[i].ftp), context).c_str());
374         }
375     }
376
377     /* other options */
378     fprintf(out, ".SH OTHER OPTIONS\n");
379     if (npargs > 0)
380     {
381         for (i = 0; (i < npargs); i++)
382         {
383             if (pa[i].type == etBOOL)
384             {
385                 fprintf(out, ".BI \"\\-[no]%s\"  \"%s\"\n %s\n\n",
386                         check(pa[i].option+1, context).c_str(),
387                         check(pa_val(&(pa[i]), tmp, 255), context).c_str(),
388                         check(pa[i].desc, context).c_str());
389             }
390             else
391             {
392                 fprintf(out, ".BI \"%s\"  \" %s\" \" %s\" \n %s\n\n",
393                         check(pa[i].option, context).c_str(),
394                         check(get_arg_desc(pa[i].type), context).c_str(),
395                         check(pa_val(&(pa[i]), tmp, 255), context).c_str(),
396                         check(pa[i].desc, context).c_str());
397             }
398         }
399     }
400
401     if (nbug > 0)
402     {
403         fprintf(out, ".SH KNOWN PROBLEMS\n");
404         for (i = 0; (i < nbug); i++)
405         {
406             fprintf(out, "\\- %s\n\n", check(bugs[i], context).c_str());
407         }
408     }
409 }
410
411 static void
412 print_tty_formatted(FILE *out, int nldesc, const char **desc,
413                     const gmx::HelpWriterContext &context)
414 {
415     char *buf;
416     int   buflen, i;
417
418     buflen = 80*nldesc;
419     snew(buf, buflen);
420     for (i = 0; (i < nldesc); i++)
421     {
422         if ((strlen(buf) > 0) &&
423             (buf[strlen(buf)-1] != ' ') && (buf[strlen(buf)-1] != '\n'))
424         {
425             strcat(buf, " ");
426         }
427         std::string temp = check(desc[i], context);
428         if (strlen(buf) + temp.length() >= (size_t)(buflen-2))
429         {
430             buflen += temp.length();
431             srenew(buf, buflen);
432         }
433         strcat(buf, temp.c_str());
434     }
435     /* Make lines of at most 79 characters */
436     char *temp = wrap_lines(buf, 78, 0, FALSE);
437     fprintf(out, "%s\n", temp);
438     sfree(temp);
439     sfree(buf);
440 }
441
442 static void write_ttyman(FILE *out,
443                          int nldesc, const char **desc,
444                          int nfile, t_filenm *fnm,
445                          int npargs, t_pargs *pa,
446                          int nbug, const char **bugs,
447                          const gmx::HelpWriterContext &context)
448 {
449     int   i;
450     char *tmp;
451
452     if (nldesc > 0)
453     {
454         fprintf(out, "DESCRIPTION\n-----------\n");
455         print_tty_formatted(out, nldesc, desc, context);
456     }
457     if (nbug > 0)
458     {
459         fprintf(out, "\n");
460         fprintf(out, "KNOWN PROBLEMS\n----------\n");
461         for (i = 0; i < nbug; i++)
462         {
463             snew(tmp, strlen(bugs[i])+3);
464             strcpy(tmp, "* ");
465             strcpy(tmp+2, check(bugs[i], context).c_str());
466             fprintf(out, "%s\n", wrap_lines(tmp, 78, 2, FALSE));
467             sfree(tmp);
468         }
469     }
470     if (nfile > 0)
471     {
472         fprintf(out, "\n");
473         pr_fns(out, nfile, fnm);
474     }
475     if (npargs > 0)
476     {
477         print_pargs(out, npargs, pa, context);
478     }
479 }
480
481 static void pr_html_files(FILE *out, int nfile, t_filenm fnm[],
482                           const gmx::HelpWriterContext &context)
483 {
484     int  i;
485     char link[10], tmp[255];
486
487     fprintf(out,
488             "<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2>\n"
489             "<TR>"
490             "<TH>option</TH>"
491             "<TH>filename</TH>"
492             "<TH>type</TH>"
493             "<TH>description</TH>"
494             "</TR>\n");
495
496     for (i = 0; (i < nfile); i++)
497     {
498         strcpy(link, ftp2ext(fnm[i].ftp));
499         if (strcmp(link, "???") == 0)
500         {
501             strcpy(link, "files");
502         }
503         fprintf(out,
504                 "<TR>"
505                 "<TD ALIGN=RIGHT> <b><tt>%s</tt></b> </TD>"
506                 "<TD ALIGN=RIGHT> <tt><a href=\"%s.html\">%12s</a></tt> </TD>"
507                 "<TD> %s </TD>"
508                 "<TD> %s </TD>"
509                 "</TR>\n",
510                 fnm[i].opt, link, fnm[i].fns[0], fileopt(fnm[i].flag, tmp),
511                 check(ftp2desc(fnm[i].ftp), context).c_str());
512     }
513     fprintf(out, "</TABLE>\n");
514 }
515
516 static void write_htmlman(FILE *out,
517                           int nldesc, const char **desc,
518                           int nfile, t_filenm *fnm,
519                           int npargs, t_pargs *pa,
520                           int nbug, const char **bugs,
521                           const gmx::HelpWriterContext &context)
522 {
523     int  i;
524     char tmp[255];
525
526     if (nldesc > 0)
527     {
528         fprintf(out, "<H3>Description</H3>\n<p>\n");
529         for (i = 0; (i < nldesc); i++)
530         {
531             fprintf(out, "%s\n", check(desc[i], context).c_str());
532         }
533     }
534     if (nfile > 0)
535     {
536         fprintf(out, "<P>\n");
537         fprintf(out, "<H3>Files</H3>\n");
538         pr_html_files(out, nfile, fnm, context);
539     }
540     if (npargs > 0)
541     {
542         fprintf(out, "<P>\n");
543         fprintf(out, "<H3>Other options</H3>\n");
544         fprintf(out,
545                 "<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2>\n"
546                 "<TR>"
547                 "<TH>option</TH>"
548                 "<TH>type</TH>"
549                 "<TH>default</TH>"
550                 "<TH>description</TH>"
551                 "</TR>\n");
552         for (i = 0; (i < npargs); i++)
553         {
554             fprintf(out,
555                     "<TR>"
556                     "<TD ALIGN=RIGHT> <b><tt>%s%s</tt></b> </TD>"
557                     "<TD ALIGN=RIGHT> %s </TD>"
558                     "<TD ALIGN=RIGHT> <tt>%s</tt> </TD>"
559                     "<TD> %s </TD>"
560                     "</TD>\n",
561                     (pa[i].type == etBOOL) ? "-[no]" : "-", pa[i].option+1,
562                     get_arg_desc(pa[i].type), pa_val(&(pa[i]), tmp, 255),
563                     check(pa[i].desc, context).c_str());
564         }
565         fprintf(out, "</TABLE>\n");
566     }
567     if (nbug > 0)
568     {
569         fprintf(out, "<P>\n");
570         fprintf(out, "<H3>Known problems</H3>\n");
571         fprintf(out, "<UL>\n");
572         for (i = 0; (i < nbug); i++)
573         {
574             fprintf(out, "<LI>%s\n", check(bugs[i], context).c_str());
575         }
576         fprintf(out, "</UL>\n");
577     }
578 }
579
580 static void pr_fopts(FILE *fp, int nf, const t_filenm tfn[], int shell)
581 {
582     switch (shell)
583     {
584         case eshellCSH:
585             for (int i = 0; i < nf; i++)
586             {
587                 fprintf(fp, " \"n/%s/f:*.", tfn[i].opt);
588                 const int ftp          = tfn[i].ftp;
589                 const int genericCount = ftp2generic_count(ftp);
590                 if (genericCount > 0)
591                 {
592                     fprintf(fp, "{");
593                     const int *const genericTypes = ftp2generic_list(ftp);
594                     for (int j = 0; j < genericCount; j++)
595                     {
596                         if (j > 0)
597                         {
598                             fprintf(fp, ",");
599                         }
600                         fprintf(fp, "%s", ftp2ext(genericTypes[j]));
601                     }
602                     fprintf(fp, "}");
603                 }
604                 else
605                 {
606                     fprintf(fp, "%s", ftp2ext(ftp));
607                 }
608                 fprintf(fp, "{");
609                 for (int j = 0; j < NZEXT; j++)
610                 {
611                     fprintf(fp, ",%s", z_ext[j]);
612                 }
613                 fprintf(fp, "}/\"");
614             }
615             break;
616         case eshellBASH:
617             for (int i = 0; i < nf; i++)
618             {
619                 fprintf(fp, "%s) COMPREPLY=( $(compgen -X '!*.", tfn[i].opt);
620                 const int ftp          = tfn[i].ftp;
621                 const int genericCount = ftp2generic_count(ftp);
622                 if (genericCount > 0)
623                 {
624                     fprintf(fp, "+(");
625                     const int *const genericTypes = ftp2generic_list(ftp);
626                     for (int j = 0; j < genericCount; j++)
627                     {
628                         if (j > 0)
629                         {
630                             fprintf(fp, "|");
631                         }
632                         fprintf(fp, "%s", ftp2ext(genericTypes[j]));
633                     }
634                     fprintf(fp, ")");
635                 }
636                 else
637                 {
638                     fprintf(fp, "%s", ftp2ext(ftp));
639                 }
640                 fprintf(fp, "*(");
641                 for (int j = 0; j < NZEXT; j++)
642                 {
643                     if (j > 0)
644                     {
645                         fprintf(fp, "|");
646                     }
647                     fprintf(fp, "%s", z_ext[j]);
648                 }
649                 fprintf(fp, ")' -f $c ; compgen -S '/' -X '.*' -d $c ));;\n");
650             }
651             break;
652         case eshellZSH:
653             for (int i = 0; i < nf; i++)
654             {
655                 fprintf(fp, "- 'c[-1,%s]' -g '*.", tfn[i].opt);
656                 const int ftp          = tfn[i].ftp;
657                 const int genericCount = ftp2generic_count(ftp);
658                 if (genericCount > 0)
659                 {
660                     fprintf(fp, "(");
661                     const int *const genericTypes = ftp2generic_list(ftp);
662                     for (int j = 0; j < genericCount; j++)
663                     {
664                         if (j > 0)
665                         {
666                             fprintf(fp, "|");
667                         }
668                         fprintf(fp, "%s", ftp2ext(genericTypes[j]));
669                     }
670                     fprintf(fp, ")");
671                 }
672                 else
673                 {
674                     fprintf(fp, "%s", ftp2ext(ftp));
675                 }
676                 fprintf(fp, "(");
677                 for (int j = 0; j < NZEXT; j++)
678                 {
679                     fprintf(fp, "|%s", z_ext[j]);
680                 }
681                 fprintf(fp, ") *(/)' ");
682             }
683             break;
684     }
685 }
686
687 static void pr_opts(FILE *fp,
688                     int nfile,  t_filenm *fnm,
689                     int npargs, t_pargs pa[], int shell)
690 {
691     int i;
692
693     switch (shell)
694     {
695         case eshellCSH:
696             fprintf(fp, " \"c/-/(");
697             for (i = 0; i < nfile; i++)
698             {
699                 fprintf(fp, " %s", fnm[i].opt+1);
700             }
701             for (i = 0; i < npargs; i++)
702             {
703                 if ( (pa[i].type == etBOOL) && *(pa[i].u.b) )
704                 {
705                     fprintf(fp, " no%s", pa[i].option+1);
706                 }
707                 else
708                 {
709                     fprintf(fp, " %s", pa[i].option+1);
710                 }
711             }
712             fprintf(fp, ")/\"");
713             break;
714         case eshellBASH:
715             fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen  -W '");
716             for (i = 0; i < nfile; i++)
717             {
718                 fprintf(fp, " -%s", fnm[i].opt+1);
719             }
720             for (i = 0; i < npargs; i++)
721             {
722                 if ( (pa[i].type == etBOOL) && *(pa[i].u.b) )
723                 {
724                     fprintf(fp, " -no%s", pa[i].option+1);
725                 }
726                 else
727                 {
728                     fprintf(fp, " -%s", pa[i].option+1);
729                 }
730             }
731             fprintf(fp, "' -- $c)); return 0; fi\n");
732             break;
733         case eshellZSH:
734             fprintf(fp, " -x 's[-]' -s \"");
735             for (i = 0; i < nfile; i++)
736             {
737                 fprintf(fp, " %s", fnm[i].opt+1);
738             }
739             for (i = 0; i < npargs; i++)
740             {
741                 if ( (pa[i].type == etBOOL) && *(pa[i].u.b) )
742                 {
743                     fprintf(fp, " no%s", pa[i].option+1);
744                 }
745                 else
746                 {
747                     fprintf(fp, " %s", pa[i].option+1);
748                 }
749             }
750             fprintf(fp, "\" ");
751             break;
752     }
753 }
754
755 static void pr_enums(FILE *fp, int npargs, t_pargs pa[], int shell)
756 {
757     int i, j;
758
759     switch (shell)
760     {
761         case eshellCSH:
762             for (i = 0; i < npargs; i++)
763             {
764                 if (pa[i].type == etENUM)
765                 {
766                     fprintf(fp, " \"n/%s/(", pa[i].option);
767                     for (j = 1; pa[i].u.c[j]; j++)
768                     {
769                         fprintf(fp, " %s", pa[i].u.c[j]);
770                     }
771                     fprintf(fp, ")/\"");
772                 }
773             }
774             break;
775         case eshellBASH:
776             for (i = 0; i < npargs; i++)
777             {
778                 if (pa[i].type == etENUM)
779                 {
780                     fprintf(fp, "%s) COMPREPLY=( $(compgen -W '", pa[i].option);
781                     for (j = 1; pa[i].u.c[j]; j++)
782                     {
783                         fprintf(fp, " %s", pa[i].u.c[j]);
784                     }
785                     fprintf(fp, " ' -- $c ));;\n");
786                 }
787             }
788             break;
789         case eshellZSH:
790             for (i = 0; i < npargs; i++)
791             {
792                 if (pa[i].type == etENUM)
793                 {
794                     fprintf(fp, "- 'c[-1,%s]' -s \"", pa[i].option);
795                     for (j = 1; pa[i].u.c[j]; j++)
796                     {
797                         fprintf(fp, " %s", pa[i].u.c[j]);
798                     }
799                     fprintf(fp, "\" ");
800                 }
801             }
802             break;
803     }
804 }
805
806 static void write_cshcompl(FILE *out,
807                            int nfile,  t_filenm *fnm,
808                            int npargs, t_pargs *pa)
809 {
810     fprintf(out, "complete %s", ShortProgram());
811     pr_enums(out, npargs, pa, eshellCSH);
812     pr_fopts(out, nfile, fnm, eshellCSH);
813     pr_opts(out, nfile, fnm, npargs, pa, eshellCSH);
814     fprintf(out, "\n");
815 }
816
817 static void write_zshcompl(FILE *out,
818                            int nfile,  t_filenm *fnm,
819                            int npargs, t_pargs *pa)
820 {
821     fprintf(out, "compctl ");
822
823     /* start with options, since they are always present */
824     pr_opts(out, nfile, fnm, npargs, pa, eshellZSH);
825     pr_enums(out, npargs, pa, eshellZSH);
826     pr_fopts(out, nfile, fnm, eshellZSH);
827     fprintf(out, "-- %s\n", ShortProgram());
828 }
829
830 static void write_bashcompl(FILE *out,
831                             int nfile,  t_filenm *fnm,
832                             int npargs, t_pargs *pa)
833 {
834     /* Advanced bash completions are handled by shell functions.
835      * p and c hold the previous and current word on the command line.
836      * We need to use extended globbing, so write it in each completion file */
837     fprintf(out, "shopt -s extglob\n");
838     fprintf(out, "_%s_compl() {\nlocal p c\n", ShortProgram());
839     fprintf(out, "COMPREPLY=() c=${COMP_WORDS[COMP_CWORD]} p=${COMP_WORDS[COMP_CWORD-1]}\n");
840     pr_opts(out, nfile, fnm, npargs, pa, eshellBASH);
841     fprintf(out, "case \"$p\" in\n");
842
843     pr_enums(out, npargs, pa, eshellBASH);
844     pr_fopts(out, nfile, fnm, eshellBASH);
845     fprintf(out, "esac }\ncomplete -F _%s_compl %s\n", ShortProgram(), ShortProgram());
846 }
847
848 void write_man(const char *mantp,
849                const char *program,
850                int nldesc, const char **desc,
851                int nfile, t_filenm *fnm,
852                int npargs, t_pargs *pa,
853                int nbug, const char **bugs)
854 {
855     bool        bHidden = false;
856     int         npar;
857     t_pargs    *par;
858
859     const gmx::CommandLineHelpContext *context
860         = gmx::GlobalCommandLineHelpContext::get();
861     bool  bFileOpened = false;
862     FILE *out;
863     if (context != NULL)
864     {
865         out     = context->writerContext().outputFile().handle();
866         bHidden = context->showHidden();
867     }
868     else
869     {
870         char buf[256];
871         sprintf(buf, "%s.%s", program, mantp);
872         out         = gmx_fio_fopen(buf, "w");
873         bFileOpened = true;
874     }
875
876     if (bHidden)
877     {
878         npar = npargs;
879         par  = pa;
880     }
881     else
882     {
883         snew(par, npargs);
884         npar = 0;
885         for (int i = 0; i < npargs; i++)
886         {
887             if (!is_hidden(&pa[i]))
888             {
889                 par[npar] = pa[i];
890                 npar++;
891             }
892         }
893     }
894
895     if (strcmp(mantp, "nroff") == 0)
896     {
897         GMX_RELEASE_ASSERT(context != NULL,
898                            "Man page export only implemented with the new context");
899         write_nroffman(out, nldesc, desc, nfile, fnm, npar, par, nbug, bugs,
900                        *context);
901     }
902     if (strcmp(mantp, "help") == 0)
903     {
904         GMX_RELEASE_ASSERT(context != NULL,
905                            "Help export only implemented with the new context");
906         write_ttyman(out, nldesc, desc, nfile, fnm, npar, par, nbug, bugs,
907                      context->writerContext());
908     }
909     if (strcmp(mantp, "html") == 0)
910     {
911         GMX_RELEASE_ASSERT(context != NULL,
912                            "HTML export only implemented with the new context");
913         write_htmlman(out, nldesc, desc, nfile, fnm, npar, par, nbug, bugs,
914                       context->writerContext());
915     }
916     if (strcmp(mantp, "completion-zsh") == 0)
917     {
918         write_zshcompl(out, nfile, fnm, npar, par);
919     }
920     if (strcmp(mantp, "completion-bash") == 0)
921     {
922         write_bashcompl(out, nfile, fnm, npar, par);
923     }
924     if (strcmp(mantp, "completion-csh") == 0)
925     {
926         write_cshcompl(out, nfile, fnm, npar, par);
927     }
928
929     if (bFileOpened)
930     {
931         gmx_fio_fclose(out);
932     }
933
934     if (!bHidden)
935     {
936         sfree(par);
937     }
938 }