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