3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
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.
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.
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.
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.
30 * For more info, check our website at http://www.gromacs.org
33 * GROningen Mixture of Alchemy and Childrens' Stories
35 #include "gromacs/commandline/wman.h"
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"
49 #include "gmx_fatal.h"
54 // Shell types, for completion stuff
56 eshellCSH, eshellBASH, eshellZSH
59 // TODO: Don't duplicate this from filenm.c/futil.c.
61 static const char *z_ext[NZEXT] = { ".gz", ".Z" };
63 /* The source code in this file should be thread-safe.
64 * Please keep it that way. */
66 static std::string check(const char *s, const gmx::HelpWriterContext &context)
68 return context.substituteMarkupAndWrapToString(gmx::TextLineWrapperSettings(), s);
71 static std::string check(const char *s, const gmx::CommandLineHelpContext &context)
73 return check(s, context.writerContext());
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.
83 static char *fileopt(unsigned long flag, char buf[])
87 if (FLAG_SET(flag, ffRW))
89 sprintf(tmp, "In/Out");
91 else if (FLAG_SET(flag, ffREAD))
93 sprintf(tmp, "Input");
95 else if (FLAG_SET(flag, ffWRITE))
97 sprintf(tmp, "Output");
101 sprintf(tmp, "Dunno");
104 if (FLAG_SET(flag, ffOPT))
106 strcat(tmp, ", Opt");
107 if (FLAG_SET(flag, ffSET))
116 if (FLAG_SET(flag, ffLIB))
118 strcat(tmp, ", Lib.");
120 if (FLAG_SET(flag, ffMULT))
122 strcat(tmp, ", Mult.");
125 sprintf(buf, "%s", tmp);
132 static void pr_fns(FILE *fp, int nf, const t_filenm tfn[])
136 char buf[256], *wbuf, opt_buf[32];
138 fprintf(fp, "%6s %12s %-12s %s\n", "Option", "Filename", "Type",
141 "------------------------------------------------------------\n");
142 for (i = 0; (i < nf); i++)
144 for (f = 0; (f < tfn[i].nfiles); f++)
146 sprintf(buf, "%4s %14s %-12s ", (f == 0) ? tfn[i].opt : "",
147 tfn[i].fns[f], (f == 0) ? fileopt(tfn[i].flag, opt_buf)
149 if (f < tfn[i].nfiles - 1)
151 fprintf(fp, "%s\n", buf);
154 if (tfn[i].nfiles > 0)
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]))))
161 for (j = strlen(tfn[i].opt); j < strlen(buf)
162 - (strlen(tfn[i].opt) - OPTLEN) + 1; j++)
164 buf[j] = buf[j + strlen(tfn[i].opt) - OPTLEN];
167 wbuf = wrap_lines(buf, 78, 35, FALSE);
168 fprintf(fp, "%s\n", wbuf);
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)
182 const char *const argtp[etNR] = {
183 "int", "step", "real", "time", "string", "bool", "vector", "enum"
188 /* Return the value of pa in the provided buffer buf, of size sz.
189 * The return value is also a pointer to buf.
191 static char *pa_val(t_pargs *pa, char buf[], int sz)
194 char buf_str[1256]; buf_str[0] = '\0';
198 GMX_RELEASE_ASSERT(sz >= 255, "Buffer must be at least 255 chars");
203 sprintf(buf, "%-d", *(pa->u.i));
205 case etGMX_LARGE_INT:
206 sprintf(buf, gmx_large_int_pfmt, *(pa->u.is));
211 sprintf(buf_str, "%-6g", r);
212 strcpy(buf, buf_str);
215 sprintf(buf, "%-6s", *(pa->u.b) ? "yes" : "no");
220 if (strlen(*(pa->u.c)) >= (size_t)sz)
222 gmx_fatal(FARGS, "Argument too long: \"%d\"\n", *(pa->u.c));
226 strcpy(buf, *(pa->u.c));
231 strcpy(buf, *(pa->u.c));
234 sprintf(buf, "%g %g %g", (*pa->u.rv)[0],
245 static char *pargs_print_line(t_pargs *pa, const gmx::HelpWriterContext &context)
247 char buf[LONGSTR], *buf2, *tmp;
249 snew(buf2, LONGSTR+strlen(pa->desc));
250 snew(tmp, LONGSTR+strlen(pa->desc));
252 if (pa->type == etBOOL)
254 sprintf(buf, "-[no]%s", pa->option+1);
258 strcpy(buf, pa->option);
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)))
263 sprintf(buf2, "%s %-6s %-6s %-s\n",
264 buf, get_arg_desc(pa->type), pa_val(pa, tmp, LONGSTR-1),
267 else if (strlen(buf) > OPTLEN)
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),
276 sprintf(buf2, "%-12s %-6s %-6s %-s\n",
277 buf, get_arg_desc(pa->type), pa_val(pa, tmp, LONGSTR-1),
282 tmp = wrap_lines(buf2, 78, 28, FALSE);
292 static void print_pargs(FILE *fp, int npargs, t_pargs pa[],
293 const gmx::HelpWriterContext &context)
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++)
302 char *wdesc = pargs_print_line(&pa[i], context);
303 fprintf(fp, "%s", wdesc);
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)
320 fprintf(out, ".SH SYNOPSIS\n");
321 fprintf(out, "\\f3%s\\fP\n", context.moduleDisplayName());
323 /* command line arguments */
326 for (i = 0; (i < nfile); i++)
328 fprintf(out, ".BI \"%s\" \" %s \"\n",
329 check(fnm[i].opt, context).c_str(),
330 check(fnm[i].fns[0], context).c_str());
335 for (i = 0; (i < npargs); i++)
337 if (pa[i].type == etBOOL)
339 fprintf(out, ".BI \"\\-[no]%s\" \"\"\n",
340 check(pa[i].option+1, context).c_str());
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());
354 fprintf(out, ".SH DESCRIPTION\n");
355 for (i = 0; (i < nldesc); i++)
357 fprintf(out, "\\&%s\n", check(desc[i], context).c_str());
364 fprintf(out, ".SH FILES\n");
365 for (i = 0; (i < nfile); i++)
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());
376 fprintf(out, ".SH OTHER OPTIONS\n");
379 for (i = 0; (i < npargs); i++)
381 if (pa[i].type == etBOOL)
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());
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());
401 fprintf(out, ".SH KNOWN PROBLEMS\n");
402 for (i = 0; (i < nbug); i++)
404 fprintf(out, "\\- %s\n\n", check(bugs[i], context).c_str());
410 print_tty_formatted(FILE *out, int nldesc, const char **desc,
411 const gmx::HelpWriterContext &context)
418 for (i = 0; (i < nldesc); i++)
420 if ((strlen(buf) > 0) &&
421 (buf[strlen(buf)-1] != ' ') && (buf[strlen(buf)-1] != '\n'))
425 std::string temp = check(desc[i], context);
426 if (strlen(buf) + temp.length() >= (size_t)(buflen-2))
428 buflen += temp.length();
431 strcat(buf, temp.c_str());
433 /* Make lines of at most 79 characters */
434 char *temp = wrap_lines(buf, 78, 0, FALSE);
435 fprintf(out, "%s\n", temp);
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)
452 fprintf(out, "DESCRIPTION\n-----------\n");
453 print_tty_formatted(out, nldesc, desc, context);
458 fprintf(out, "KNOWN PROBLEMS\n----------\n");
459 for (i = 0; i < nbug; i++)
461 snew(tmp, strlen(bugs[i])+3);
463 strcpy(tmp+2, check(bugs[i], context).c_str());
464 fprintf(out, "%s\n", wrap_lines(tmp, 78, 2, FALSE));
471 pr_fns(out, nfile, fnm);
475 print_pargs(out, npargs, pa, context);
479 static void pr_html_files(FILE *out, int nfile, t_filenm fnm[],
480 const gmx::HelpWriterContext &context)
483 char link[10], tmp[255];
486 "<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2>\n"
491 "<TH>description</TH>"
494 for (i = 0; (i < nfile); i++)
496 strcpy(link, ftp2ext(fnm[i].ftp));
497 if (strcmp(link, "???") == 0)
499 strcpy(link, "files");
503 "<TD ALIGN=RIGHT> <b><tt>%s</tt></b> </TD>"
504 "<TD ALIGN=RIGHT> <tt><a href=\"%s.html\">%12s</a></tt> </TD>"
508 fnm[i].opt, link, fnm[i].fns[0], fileopt(fnm[i].flag, tmp),
509 check(ftp2desc(fnm[i].ftp), context).c_str());
511 fprintf(out, "</TABLE>\n");
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)
526 fprintf(out, "<H3>Description</H3>\n<p>\n");
527 for (i = 0; (i < nldesc); i++)
529 fprintf(out, "%s\n", check(desc[i], context).c_str());
534 fprintf(out, "<P>\n");
535 fprintf(out, "<H3>Files</H3>\n");
536 pr_html_files(out, nfile, fnm, context);
540 fprintf(out, "<P>\n");
541 fprintf(out, "<H3>Other options</H3>\n");
543 "<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2>\n"
548 "<TH>description</TH>"
550 for (i = 0; (i < npargs); i++)
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>"
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());
563 fprintf(out, "</TABLE>\n");
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++)
572 fprintf(out, "<LI>%s\n", check(bugs[i], context).c_str());
574 fprintf(out, "</UL>\n");
578 static void pr_fopts(FILE *fp, int nf, const t_filenm tfn[], int shell)
583 for (int i = 0; i < nf; i++)
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)
591 const int *const genericTypes = ftp2generic_list(ftp);
592 for (int j = 0; j < genericCount; j++)
598 fprintf(fp, "%s", ftp2ext(genericTypes[j]));
604 fprintf(fp, "%s", ftp2ext(ftp));
607 for (int j = 0; j < NZEXT; j++)
609 fprintf(fp, ",%s", z_ext[j]);
615 for (int i = 0; i < nf; i++)
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)
623 const int *const genericTypes = ftp2generic_list(ftp);
624 for (int j = 0; j < genericCount; j++)
630 fprintf(fp, "%s", ftp2ext(genericTypes[j]));
636 fprintf(fp, "%s", ftp2ext(ftp));
639 for (int j = 0; j < NZEXT; j++)
645 fprintf(fp, "%s", z_ext[j]);
647 fprintf(fp, ")' -f $c ; compgen -S '/' -X '.*' -d $c ));;\n");
651 for (int i = 0; i < nf; i++)
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)
659 const int *const genericTypes = ftp2generic_list(ftp);
660 for (int j = 0; j < genericCount; j++)
666 fprintf(fp, "%s", ftp2ext(genericTypes[j]));
672 fprintf(fp, "%s", ftp2ext(ftp));
675 for (int j = 0; j < NZEXT; j++)
677 fprintf(fp, "|%s", z_ext[j]);
679 fprintf(fp, ") *(/)' ");
685 static void pr_opts(FILE *fp,
686 int nfile, t_filenm *fnm,
687 int npargs, t_pargs pa[], int shell)
694 fprintf(fp, " \"c/-/(");
695 for (i = 0; i < nfile; i++)
697 fprintf(fp, " %s", fnm[i].opt+1);
699 for (i = 0; i < npargs; i++)
701 if ( (pa[i].type == etBOOL) && *(pa[i].u.b) )
703 fprintf(fp, " no%s", pa[i].option+1);
707 fprintf(fp, " %s", pa[i].option+1);
713 fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen -W '");
714 for (i = 0; i < nfile; i++)
716 fprintf(fp, " -%s", fnm[i].opt+1);
718 for (i = 0; i < npargs; i++)
720 if ( (pa[i].type == etBOOL) && *(pa[i].u.b) )
722 fprintf(fp, " -no%s", pa[i].option+1);
726 fprintf(fp, " -%s", pa[i].option+1);
729 fprintf(fp, "' -- $c)); return 0; fi\n");
732 fprintf(fp, " -x 's[-]' -s \"");
733 for (i = 0; i < nfile; i++)
735 fprintf(fp, " %s", fnm[i].opt+1);
737 for (i = 0; i < npargs; i++)
739 if ( (pa[i].type == etBOOL) && *(pa[i].u.b) )
741 fprintf(fp, " no%s", pa[i].option+1);
745 fprintf(fp, " %s", pa[i].option+1);
753 static void pr_enums(FILE *fp, int npargs, t_pargs pa[], int shell)
760 for (i = 0; i < npargs; i++)
762 if (pa[i].type == etENUM)
764 fprintf(fp, " \"n/%s/(", pa[i].option);
765 for (j = 1; pa[i].u.c[j]; j++)
767 fprintf(fp, " %s", pa[i].u.c[j]);
774 for (i = 0; i < npargs; i++)
776 if (pa[i].type == etENUM)
778 fprintf(fp, "%s) COMPREPLY=( $(compgen -W '", pa[i].option);
779 for (j = 1; pa[i].u.c[j]; j++)
781 fprintf(fp, " %s", pa[i].u.c[j]);
783 fprintf(fp, " ' -- $c ));;\n");
788 for (i = 0; i < npargs; i++)
790 if (pa[i].type == etENUM)
792 fprintf(fp, "- 'c[-1,%s]' -s \"", pa[i].option);
793 for (j = 1; pa[i].u.c[j]; j++)
795 fprintf(fp, " %s", pa[i].u.c[j]);
804 static void write_cshcompl(FILE *out,
805 int nfile, t_filenm *fnm,
806 int npargs, t_pargs *pa)
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);
815 static void write_zshcompl(FILE *out,
816 int nfile, t_filenm *fnm,
817 int npargs, t_pargs *pa)
819 fprintf(out, "compctl ");
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());
828 static void write_bashcompl(FILE *out,
829 int nfile, t_filenm *fnm,
830 int npargs, t_pargs *pa)
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");
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());
846 void write_man(const char *mantp,
848 int nldesc, const char **desc,
849 int nfile, t_filenm *fnm,
850 int npargs, t_pargs *pa,
851 int nbug, const char **bugs)
853 bool bHidden = false;
857 const gmx::CommandLineHelpContext *context
858 = gmx::GlobalCommandLineHelpContext::get();
859 bool bFileOpened = false;
863 out = context->writerContext().outputFile().handle();
864 bHidden = context->showHidden();
869 sprintf(buf, "%s.%s", program, mantp);
870 out = gmx_fio_fopen(buf, "w");
883 for (int i = 0; i < npargs; i++)
885 if (!is_hidden(&pa[i]))
893 if (strcmp(mantp, "nroff") == 0)
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,
900 if (strcmp(mantp, "help") == 0)
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());
907 if (strcmp(mantp, "html") == 0)
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());
914 if (strcmp(mantp, "completion-zsh") == 0)
916 write_zshcompl(out, nfile, fnm, npar, par);
918 if (strcmp(mantp, "completion-bash") == 0)
920 write_bashcompl(out, nfile, fnm, npar, par);
922 if (strcmp(mantp, "completion-csh") == 0)
924 write_cshcompl(out, nfile, fnm, npar, par);