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) 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.
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/wman.h"
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"
52 #include "gmx_fatal.h"
56 // Shell types, for completion stuff
58 eshellCSH, eshellBASH, eshellZSH
61 // TODO: Don't duplicate this from filenm.c/futil.c.
63 static const char *z_ext[NZEXT] = { ".gz", ".Z" };
65 /* The source code in this file should be thread-safe.
66 * Please keep it that way. */
68 static std::string check(const char *s, const gmx::HelpWriterContext &context)
70 return context.substituteMarkupAndWrapToString(gmx::TextLineWrapperSettings(), s);
73 static std::string check(const char *s, const gmx::CommandLineHelpContext &context)
75 return check(s, context.writerContext());
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.
85 static char *fileopt(unsigned long flag, char buf[])
89 if (FLAG_SET(flag, ffRW))
91 sprintf(tmp, "In/Out");
93 else if (FLAG_SET(flag, ffREAD))
95 sprintf(tmp, "Input");
97 else if (FLAG_SET(flag, ffWRITE))
99 sprintf(tmp, "Output");
103 sprintf(tmp, "Dunno");
106 if (FLAG_SET(flag, ffOPT))
108 strcat(tmp, ", Opt");
109 if (FLAG_SET(flag, ffSET))
118 if (FLAG_SET(flag, ffLIB))
120 strcat(tmp, ", Lib.");
122 if (FLAG_SET(flag, ffMULT))
124 strcat(tmp, ", Mult.");
127 sprintf(buf, "%s", tmp);
134 static void pr_fns(FILE *fp, int nf, const t_filenm tfn[])
138 char buf[256], *wbuf, opt_buf[32];
140 fprintf(fp, "%6s %12s %-12s %s\n", "Option", "Filename", "Type",
143 "------------------------------------------------------------\n");
144 for (i = 0; (i < nf); i++)
146 for (f = 0; (f < tfn[i].nfiles); f++)
148 sprintf(buf, "%4s %14s %-12s ", (f == 0) ? tfn[i].opt : "",
149 tfn[i].fns[f], (f == 0) ? fileopt(tfn[i].flag, opt_buf)
151 if (f < tfn[i].nfiles - 1)
153 fprintf(fp, "%s\n", buf);
156 if (tfn[i].nfiles > 0)
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]))))
163 for (j = strlen(tfn[i].opt); j < strlen(buf)
164 - (strlen(tfn[i].opt) - OPTLEN) + 1; j++)
166 buf[j] = buf[j + strlen(tfn[i].opt) - OPTLEN];
169 wbuf = wrap_lines(buf, 78, 35, FALSE);
170 fprintf(fp, "%s\n", wbuf);
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)
184 const char *const argtp[etNR] = {
185 "int", "step", "real", "time", "string", "bool", "vector", "enum"
190 /* Return the value of pa in the provided buffer buf, of size sz.
191 * The return value is also a pointer to buf.
193 static char *pa_val(t_pargs *pa, char buf[], int sz)
196 char buf_str[1256]; buf_str[0] = '\0';
200 GMX_RELEASE_ASSERT(sz >= 255, "Buffer must be at least 255 chars");
205 sprintf(buf, "%-d", *(pa->u.i));
207 case etGMX_LARGE_INT:
208 sprintf(buf, gmx_large_int_pfmt, *(pa->u.is));
213 sprintf(buf_str, "%-6g", r);
214 strcpy(buf, buf_str);
217 sprintf(buf, "%-6s", *(pa->u.b) ? "yes" : "no");
222 if (strlen(*(pa->u.c)) >= (size_t)sz)
224 gmx_fatal(FARGS, "Argument too long: \"%d\"\n", *(pa->u.c));
228 strcpy(buf, *(pa->u.c));
233 strcpy(buf, *(pa->u.c));
236 sprintf(buf, "%g %g %g", (*pa->u.rv)[0],
247 static char *pargs_print_line(t_pargs *pa, const gmx::HelpWriterContext &context)
249 char buf[LONGSTR], *buf2, *tmp;
251 snew(buf2, LONGSTR+strlen(pa->desc));
252 snew(tmp, LONGSTR+strlen(pa->desc));
254 if (pa->type == etBOOL)
256 sprintf(buf, "-[no]%s", pa->option+1);
260 strcpy(buf, pa->option);
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)))
265 sprintf(buf2, "%s %-6s %-6s %-s\n",
266 buf, get_arg_desc(pa->type), pa_val(pa, tmp, LONGSTR-1),
269 else if (strlen(buf) > OPTLEN)
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),
278 sprintf(buf2, "%-12s %-6s %-6s %-s\n",
279 buf, get_arg_desc(pa->type), pa_val(pa, tmp, LONGSTR-1),
284 tmp = wrap_lines(buf2, 78, 28, FALSE);
294 static void print_pargs(FILE *fp, int npargs, t_pargs pa[],
295 const gmx::HelpWriterContext &context)
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++)
304 char *wdesc = pargs_print_line(&pa[i], context);
305 fprintf(fp, "%s", wdesc);
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)
322 fprintf(out, ".SH SYNOPSIS\n");
323 fprintf(out, "\\f3%s\\fP\n", context.moduleDisplayName());
325 /* command line arguments */
328 for (i = 0; (i < nfile); i++)
330 fprintf(out, ".BI \"%s\" \" %s \"\n",
331 check(fnm[i].opt, context).c_str(),
332 check(fnm[i].fns[0], context).c_str());
337 for (i = 0; (i < npargs); i++)
339 if (pa[i].type == etBOOL)
341 fprintf(out, ".BI \"\\-[no]%s\" \"\"\n",
342 check(pa[i].option+1, context).c_str());
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());
356 fprintf(out, ".SH DESCRIPTION\n");
357 for (i = 0; (i < nldesc); i++)
359 fprintf(out, "\\&%s\n", check(desc[i], context).c_str());
366 fprintf(out, ".SH FILES\n");
367 for (i = 0; (i < nfile); i++)
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());
378 fprintf(out, ".SH OTHER OPTIONS\n");
381 for (i = 0; (i < npargs); i++)
383 if (pa[i].type == etBOOL)
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());
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());
403 fprintf(out, ".SH KNOWN PROBLEMS\n");
404 for (i = 0; (i < nbug); i++)
406 fprintf(out, "\\- %s\n\n", check(bugs[i], context).c_str());
412 print_tty_formatted(FILE *out, int nldesc, const char **desc,
413 const gmx::HelpWriterContext &context)
420 for (i = 0; (i < nldesc); i++)
422 if ((strlen(buf) > 0) &&
423 (buf[strlen(buf)-1] != ' ') && (buf[strlen(buf)-1] != '\n'))
427 std::string temp = check(desc[i], context);
428 if (strlen(buf) + temp.length() >= (size_t)(buflen-2))
430 buflen += temp.length();
433 strcat(buf, temp.c_str());
435 /* Make lines of at most 79 characters */
436 char *temp = wrap_lines(buf, 78, 0, FALSE);
437 fprintf(out, "%s\n", temp);
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)
454 fprintf(out, "DESCRIPTION\n-----------\n");
455 print_tty_formatted(out, nldesc, desc, context);
460 fprintf(out, "KNOWN PROBLEMS\n----------\n");
461 for (i = 0; i < nbug; i++)
463 snew(tmp, strlen(bugs[i])+3);
465 strcpy(tmp+2, check(bugs[i], context).c_str());
466 fprintf(out, "%s\n", wrap_lines(tmp, 78, 2, FALSE));
473 pr_fns(out, nfile, fnm);
477 print_pargs(out, npargs, pa, context);
481 static void pr_html_files(FILE *out, int nfile, t_filenm fnm[],
482 const gmx::HelpWriterContext &context)
485 char link[10], tmp[255];
488 "<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2>\n"
493 "<TH>description</TH>"
496 for (i = 0; (i < nfile); i++)
498 strcpy(link, ftp2ext(fnm[i].ftp));
499 if (strcmp(link, "???") == 0)
501 strcpy(link, "files");
505 "<TD ALIGN=RIGHT> <b><tt>%s</tt></b> </TD>"
506 "<TD ALIGN=RIGHT> <tt><a href=\"%s.html\">%12s</a></tt> </TD>"
510 fnm[i].opt, link, fnm[i].fns[0], fileopt(fnm[i].flag, tmp),
511 check(ftp2desc(fnm[i].ftp), context).c_str());
513 fprintf(out, "</TABLE>\n");
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)
528 fprintf(out, "<H3>Description</H3>\n<p>\n");
529 for (i = 0; (i < nldesc); i++)
531 fprintf(out, "%s\n", check(desc[i], context).c_str());
536 fprintf(out, "<P>\n");
537 fprintf(out, "<H3>Files</H3>\n");
538 pr_html_files(out, nfile, fnm, context);
542 fprintf(out, "<P>\n");
543 fprintf(out, "<H3>Other options</H3>\n");
545 "<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2>\n"
550 "<TH>description</TH>"
552 for (i = 0; (i < npargs); i++)
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>"
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());
565 fprintf(out, "</TABLE>\n");
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++)
574 fprintf(out, "<LI>%s\n", check(bugs[i], context).c_str());
576 fprintf(out, "</UL>\n");
580 static void pr_fopts(FILE *fp, int nf, const t_filenm tfn[], int shell)
585 for (int i = 0; i < nf; i++)
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)
593 const int *const genericTypes = ftp2generic_list(ftp);
594 for (int j = 0; j < genericCount; j++)
600 fprintf(fp, "%s", ftp2ext(genericTypes[j]));
606 fprintf(fp, "%s", ftp2ext(ftp));
609 for (int j = 0; j < NZEXT; j++)
611 fprintf(fp, ",%s", z_ext[j]);
617 for (int i = 0; i < nf; i++)
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)
625 const int *const genericTypes = ftp2generic_list(ftp);
626 for (int j = 0; j < genericCount; j++)
632 fprintf(fp, "%s", ftp2ext(genericTypes[j]));
638 fprintf(fp, "%s", ftp2ext(ftp));
641 for (int j = 0; j < NZEXT; j++)
647 fprintf(fp, "%s", z_ext[j]);
649 fprintf(fp, ")' -f $c ; compgen -S '/' -X '.*' -d $c ));;\n");
653 for (int i = 0; i < nf; i++)
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)
661 const int *const genericTypes = ftp2generic_list(ftp);
662 for (int j = 0; j < genericCount; j++)
668 fprintf(fp, "%s", ftp2ext(genericTypes[j]));
674 fprintf(fp, "%s", ftp2ext(ftp));
677 for (int j = 0; j < NZEXT; j++)
679 fprintf(fp, "|%s", z_ext[j]);
681 fprintf(fp, ") *(/)' ");
687 static void pr_opts(FILE *fp,
688 int nfile, t_filenm *fnm,
689 int npargs, t_pargs pa[], int shell)
696 fprintf(fp, " \"c/-/(");
697 for (i = 0; i < nfile; i++)
699 fprintf(fp, " %s", fnm[i].opt+1);
701 for (i = 0; i < npargs; i++)
703 if ( (pa[i].type == etBOOL) && *(pa[i].u.b) )
705 fprintf(fp, " no%s", pa[i].option+1);
709 fprintf(fp, " %s", pa[i].option+1);
715 fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen -W '");
716 for (i = 0; i < nfile; i++)
718 fprintf(fp, " -%s", fnm[i].opt+1);
720 for (i = 0; i < npargs; i++)
722 if ( (pa[i].type == etBOOL) && *(pa[i].u.b) )
724 fprintf(fp, " -no%s", pa[i].option+1);
728 fprintf(fp, " -%s", pa[i].option+1);
731 fprintf(fp, "' -- $c)); return 0; fi\n");
734 fprintf(fp, " -x 's[-]' -s \"");
735 for (i = 0; i < nfile; i++)
737 fprintf(fp, " %s", fnm[i].opt+1);
739 for (i = 0; i < npargs; i++)
741 if ( (pa[i].type == etBOOL) && *(pa[i].u.b) )
743 fprintf(fp, " no%s", pa[i].option+1);
747 fprintf(fp, " %s", pa[i].option+1);
755 static void pr_enums(FILE *fp, int npargs, t_pargs pa[], int shell)
762 for (i = 0; i < npargs; i++)
764 if (pa[i].type == etENUM)
766 fprintf(fp, " \"n/%s/(", pa[i].option);
767 for (j = 1; pa[i].u.c[j]; j++)
769 fprintf(fp, " %s", pa[i].u.c[j]);
776 for (i = 0; i < npargs; i++)
778 if (pa[i].type == etENUM)
780 fprintf(fp, "%s) COMPREPLY=( $(compgen -W '", pa[i].option);
781 for (j = 1; pa[i].u.c[j]; j++)
783 fprintf(fp, " %s", pa[i].u.c[j]);
785 fprintf(fp, " ' -- $c ));;\n");
790 for (i = 0; i < npargs; i++)
792 if (pa[i].type == etENUM)
794 fprintf(fp, "- 'c[-1,%s]' -s \"", pa[i].option);
795 for (j = 1; pa[i].u.c[j]; j++)
797 fprintf(fp, " %s", pa[i].u.c[j]);
806 static void write_cshcompl(FILE *out,
807 int nfile, t_filenm *fnm,
808 int npargs, t_pargs *pa)
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);
817 static void write_zshcompl(FILE *out,
818 int nfile, t_filenm *fnm,
819 int npargs, t_pargs *pa)
821 fprintf(out, "compctl ");
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());
830 static void write_bashcompl(FILE *out,
831 int nfile, t_filenm *fnm,
832 int npargs, t_pargs *pa)
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");
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());
848 void write_man(const char *mantp,
850 int nldesc, const char **desc,
851 int nfile, t_filenm *fnm,
852 int npargs, t_pargs *pa,
853 int nbug, const char **bugs)
855 bool bHidden = false;
859 const gmx::CommandLineHelpContext *context
860 = gmx::GlobalCommandLineHelpContext::get();
861 bool bFileOpened = false;
865 out = context->writerContext().outputFile().handle();
866 bHidden = context->showHidden();
871 sprintf(buf, "%s.%s", program, mantp);
872 out = gmx_fio_fopen(buf, "w");
885 for (int i = 0; i < npargs; i++)
887 if (!is_hidden(&pa[i]))
895 if (strcmp(mantp, "nroff") == 0)
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,
902 if (strcmp(mantp, "help") == 0)
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());
909 if (strcmp(mantp, "html") == 0)
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());
916 if (strcmp(mantp, "completion-zsh") == 0)
918 write_zshcompl(out, nfile, fnm, npar, par);
920 if (strcmp(mantp, "completion-bash") == 0)
922 write_bashcompl(out, nfile, fnm, npar, par);
924 if (strcmp(mantp, "completion-csh") == 0)
926 write_cshcompl(out, nfile, fnm, npar, par);