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) 2013,2014,2015,2016,2017, 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.
47 #include "gromacs/commandline/cmdlinehelpcontext.h"
48 #include "gromacs/commandline/cmdlinehelpwriter.h"
49 #include "gromacs/commandline/cmdlineparser.h"
50 #include "gromacs/fileio/oenv.h"
51 #include "gromacs/fileio/timecontrol.h"
52 #include "gromacs/options/basicoptions.h"
53 #include "gromacs/options/behaviorcollection.h"
54 #include "gromacs/options/filenameoption.h"
55 #include "gromacs/options/filenameoptionmanager.h"
56 #include "gromacs/options/options.h"
57 #include "gromacs/options/timeunitmanager.h"
58 #include "gromacs/utility/arrayref.h"
59 #include "gromacs/utility/basenetwork.h"
60 #include "gromacs/utility/classhelpers.h"
61 #include "gromacs/utility/cstringutil.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/fatalerror.h"
64 #include "gromacs/utility/gmxassert.h"
65 #include "gromacs/utility/path.h"
66 #include "gromacs/utility/programcontext.h"
67 #include "gromacs/utility/smalloc.h"
68 #include "gromacs/utility/stringutil.h"
70 /* The source code in this file should be thread-safe.
71 Please keep it that way. */
73 int nenum(const char *const enumc
[])
78 /* we *can* compare pointers directly here! */
79 while (enumc
[i
] && enumc
[0] != enumc
[i
])
87 int opt2parg_int(const char *option
, int nparg
, t_pargs pa
[])
91 for (i
= 0; (i
< nparg
); i
++)
93 if (strcmp(pa
[i
].option
, option
) == 0)
99 gmx_fatal(FARGS
, "No integer option %s in pargs", option
);
104 gmx_bool
opt2parg_bool(const char *option
, int nparg
, t_pargs pa
[])
108 for (i
= 0; (i
< nparg
); i
++)
110 if (strcmp(pa
[i
].option
, option
) == 0)
116 gmx_fatal(FARGS
, "No boolean option %s in pargs", option
);
121 real
opt2parg_real(const char *option
, int nparg
, t_pargs pa
[])
125 for (i
= 0; (i
< nparg
); i
++)
127 if (strcmp(pa
[i
].option
, option
) == 0)
133 gmx_fatal(FARGS
, "No real option %s in pargs", option
);
138 const char *opt2parg_str(const char *option
, int nparg
, t_pargs pa
[])
142 for (i
= 0; (i
< nparg
); i
++)
144 if (strcmp(pa
[i
].option
, option
) == 0)
150 gmx_fatal(FARGS
, "No string option %s in pargs", option
);
155 gmx_bool
opt2parg_bSet(const char *option
, int nparg
, t_pargs pa
[])
159 for (i
= 0; (i
< nparg
); i
++)
161 if (strcmp(pa
[i
].option
, option
) == 0)
167 gmx_fatal(FARGS
, "No such option %s in pargs", option
);
169 return FALSE
; /* Too make some compilers happy */
172 const char *opt2parg_enum(const char *option
, int nparg
, t_pargs pa
[])
176 for (i
= 0; (i
< nparg
); i
++)
178 if (strcmp(pa
[i
].option
, option
) == 0)
184 gmx_fatal(FARGS
, "No such option %s in pargs", option
);
189 /********************************************************************
190 * parse_common_args()
200 * Returns the index of the default xvg format.
202 * \ingroup module_commandline
204 int getDefaultXvgFormat(gmx::ConstArrayRef
<const char *> xvgFormats
)
206 const char *const select
= getenv("GMX_VIEW_XVG");
207 if (select
!= nullptr)
209 ConstArrayRef
<const char *>::const_iterator i
=
210 std::find(xvgFormats
.begin(), xvgFormats
.end(), std::string(select
));
211 if (i
!= xvgFormats
.end())
213 return std::distance(xvgFormats
.begin(), i
);
220 /* The default is the first option */
225 * Conversion helper between t_pargs/t_filenm and Options.
227 * This class holds the necessary mapping between the old C structures and
228 * the new C++ options to allow copying values back after parsing for cases
229 * where the C++ options do not directly provide the type of value required for
232 * \ingroup module_commandline
238 * Initializes the adapter to convert from a specified command line.
240 * The command line is required, because t_pargs wants to return
241 * strings by reference to the original command line.
242 * OptionsAdapter creates a copy of the `argv` array (but not the
243 * strings) to make this possible, even if the parser removes
244 * options it has recognized.
246 OptionsAdapter(int argc
, const char *const argv
[])
247 : argv_(argv
, argv
+ argc
)
252 * Converts a t_filenm option into an Options option.
254 * \param options Options object to add the new option to.
255 * \param fnm t_filenm option to convert.
257 void filenmToOptions(Options
*options
, t_filenm
*fnm
);
259 * Converts a t_pargs option into an Options option.
261 * \param options Options object to add the new option to.
262 * \param pa t_pargs option to convert.
264 void pargsToOptions(Options
*options
, t_pargs
*pa
);
267 * Copies values back from options to t_pargs/t_filenm.
269 void copyValues(bool bReadNode
);
274 //! Creates a conversion helper for a given `t_filenm` struct.
275 explicit FileNameData(t_filenm
*fnm
) : fnm(fnm
), optionInfo(nullptr)
279 //! t_filenm structure to receive the final values.
281 //! Option info object for the created FileNameOption.
282 FileNameOptionInfo
*optionInfo
;
283 //! Value storage for the created FileNameOption.
284 std::vector
<std::string
> values
;
286 struct ProgramArgData
288 //! Creates a conversion helper for a given `t_pargs` struct.
289 explicit ProgramArgData(t_pargs
*pa
)
290 : pa(pa
), optionInfo(nullptr), enumIndex(0), boolValue(false)
294 //! t_pargs structure to receive the final values.
296 //! Option info object for the created option.
297 OptionInfo
*optionInfo
;
298 //! Value storage for a non-enum StringOption (unused for other types).
299 std::string stringValue
;
300 //! Value storage for an enum option (unused for other types).
302 //! Value storage for a BooleanOption (unused for other types).
306 std::vector
<const char *> argv_
;
307 // These are lists instead of vectors to avoid relocating existing
308 // objects in case the container is reallocated (the Options object
309 // contains pointes to members of the objects, which would get
311 std::list
<FileNameData
> fileNameOptions_
;
312 std::list
<ProgramArgData
> programArgs_
;
314 GMX_DISALLOW_COPY_AND_ASSIGN(OptionsAdapter
);
317 void OptionsAdapter::filenmToOptions(Options
*options
, t_filenm
*fnm
)
319 if (fnm
->opt
== nullptr)
321 // Existing code may use opt2fn() instead of ftp2fn() for
322 // options that use the default option name, so we need to
323 // keep the old behavior instead of fixing opt2fn().
324 // TODO: Check that this is not the case, remove this, and make
325 // opt2*() work even if fnm->opt is NULL for some options.
326 fnm
->opt
= ftp2defopt(fnm
->ftp
);
328 const bool bRead
= ((fnm
->flag
& ffREAD
) != 0);
329 const bool bWrite
= ((fnm
->flag
& ffWRITE
) != 0);
330 const bool bOptional
= ((fnm
->flag
& ffOPT
) != 0);
331 const bool bLibrary
= ((fnm
->flag
& ffLIB
) != 0);
332 const bool bMultiple
= ((fnm
->flag
& ffMULT
) != 0);
333 const bool bMissing
= ((fnm
->flag
& ffALLOW_MISSING
) != 0);
334 const char *const name
= &fnm
->opt
[1];
335 const char * defName
= fnm
->fn
;
337 if (defName
== nullptr)
339 defName
= ftp2defnm(fnm
->ftp
);
341 else if (Path::hasExtension(defName
))
343 defType
= fn2ftp(defName
);
344 GMX_RELEASE_ASSERT(defType
!= efNR
,
345 "File name option specifies an invalid extension");
347 fileNameOptions_
.emplace_back(fnm
);
348 FileNameData
&data
= fileNameOptions_
.back();
349 data
.optionInfo
= options
->addOption(
350 FileNameOption(name
).storeVector(&data
.values
)
351 .defaultBasename(defName
).defaultType(defType
)
352 .legacyType(fnm
->ftp
).legacyOptionalBehavior()
353 .readWriteFlags(bRead
, bWrite
).required(!bOptional
)
354 .libraryFile(bLibrary
).multiValue(bMultiple
)
355 .allowMissing(bMissing
)
356 .description(ftp2desc(fnm
->ftp
)));
359 void OptionsAdapter::pargsToOptions(Options
*options
, t_pargs
*pa
)
361 const bool bHidden
= startsWith(pa
->desc
, "HIDDEN");
362 const char *const name
= &pa
->option
[1];
363 const char *const desc
= (bHidden
? &pa
->desc
[6] : pa
->desc
);
364 programArgs_
.emplace_back(pa
);
365 ProgramArgData
&data
= programArgs_
.back();
369 data
.optionInfo
= options
->addOption(
370 IntegerOption(name
).store(pa
->u
.i
)
371 .description(desc
).hidden(bHidden
));
374 data
.optionInfo
= options
->addOption(
375 Int64Option(name
).store(pa
->u
.is
)
376 .description(desc
).hidden(bHidden
));
379 data
.optionInfo
= options
->addOption(
380 RealOption(name
).store(pa
->u
.r
)
381 .description(desc
).hidden(bHidden
));
384 data
.optionInfo
= options
->addOption(
385 RealOption(name
).store(pa
->u
.r
).timeValue()
386 .description(desc
).hidden(bHidden
));
390 const char *const defValue
= (*pa
->u
.c
!= nullptr ? *pa
->u
.c
: "");
391 data
.optionInfo
= options
->addOption(
392 StringOption(name
).store(&data
.stringValue
)
393 .defaultValue(defValue
)
394 .description(desc
).hidden(bHidden
));
398 data
.optionInfo
= options
->addOption(
399 BooleanOption(name
).store(&data
.boolValue
)
400 .defaultValue(*pa
->u
.b
)
401 .description(desc
).hidden(bHidden
));
404 data
.optionInfo
= options
->addOption(
405 RealOption(name
).store(*pa
->u
.rv
).vector()
406 .description(desc
).hidden(bHidden
));
410 const int defaultIndex
= (pa
->u
.c
[0] != nullptr ? nenum(pa
->u
.c
) - 1 : 0);
411 data
.optionInfo
= options
->addOption(
412 EnumIntOption(name
).store(&data
.enumIndex
)
413 .defaultValue(defaultIndex
)
414 .enumValueFromNullTerminatedArray(pa
->u
.c
+ 1)
415 .description(desc
).hidden(bHidden
));
419 GMX_THROW(NotImplementedError("Argument type not implemented"));
422 void OptionsAdapter::copyValues(bool bReadNode
)
424 std::list
<FileNameData
>::const_iterator file
;
425 for (file
= fileNameOptions_
.begin(); file
!= fileNameOptions_
.end(); ++file
)
427 if (!bReadNode
&& (file
->fnm
->flag
& ffREAD
))
431 if (file
->optionInfo
->isSet())
433 file
->fnm
->flag
|= ffSET
;
435 file
->fnm
->nfiles
= file
->values
.size();
436 snew(file
->fnm
->fns
, file
->fnm
->nfiles
);
437 for (int i
= 0; i
< file
->fnm
->nfiles
; ++i
)
439 file
->fnm
->fns
[i
] = gmx_strdup(file
->values
[i
].c_str());
442 std::list
<ProgramArgData
>::const_iterator arg
;
443 for (arg
= programArgs_
.begin(); arg
!= programArgs_
.end(); ++arg
)
445 arg
->pa
->bSet
= arg
->optionInfo
->isSet();
446 switch (arg
->pa
->type
)
452 std::vector
<const char *>::const_iterator pos
=
453 std::find(argv_
.begin(), argv_
.end(), arg
->stringValue
);
454 GMX_RELEASE_ASSERT(pos
!= argv_
.end(),
455 "String argument got a value not in argv");
456 *arg
->pa
->u
.c
= *pos
;
461 *arg
->pa
->u
.b
= arg
->boolValue
;
464 *arg
->pa
->u
.c
= arg
->pa
->u
.c
[arg
->enumIndex
+ 1];
467 // For other types, there is nothing type-specific to do.
477 gmx_bool
parse_common_args(int *argc
, char *argv
[], unsigned long Flags
,
478 int nfile
, t_filenm fnm
[], int npargs
, t_pargs
*pa
,
479 int ndesc
, const char **desc
,
480 int nbugs
, const char **bugs
,
481 gmx_output_env_t
**oenv
)
483 /* This array should match the order of the enum in oenv.h */
484 const char *const xvg_formats
[] = { "xmgrace", "xmgr", "none" };
486 // Handle the flags argument, which is a bit field
487 // The FF macro returns whether or not the bit is set
488 #define FF(arg) ((Flags & arg) == arg)
492 double tbegin
= 0.0, tend
= 0.0, tdelta
= 0.0;
493 bool bBeginTimeSet
= false, bEndTimeSet
= false, bDtSet
= false;
496 gmx::OptionsAdapter
adapter(*argc
, argv
);
497 gmx::Options options
;
498 gmx::OptionsBehaviorCollection
behaviors(&options
);
499 gmx::FileNameOptionManager fileOptManager
;
501 fileOptManager
.disableInputOptionChecking(
502 FF(PCA_NOT_READ_NODE
) || FF(PCA_DISABLE_INPUT_FILE_CHECKING
));
503 options
.addManager(&fileOptManager
);
505 if (FF(PCA_CAN_SET_DEFFNM
))
507 fileOptManager
.addDefaultFileNameOption(&options
, "deffnm");
509 if (FF(PCA_CAN_BEGIN
))
512 gmx::DoubleOption("b")
513 .store(&tbegin
).storeIsSet(&bBeginTimeSet
).timeValue()
514 .description("First frame (%t) to read from trajectory"));
519 gmx::DoubleOption("e")
520 .store(&tend
).storeIsSet(&bEndTimeSet
).timeValue()
521 .description("Last frame (%t) to read from trajectory"));
526 gmx::DoubleOption("dt")
527 .store(&tdelta
).storeIsSet(&bDtSet
).timeValue()
528 .description("Only use frame when t MOD dt = first time (%t)"));
530 gmx::TimeUnit timeUnit
= gmx::TimeUnit_Default
;
531 if (FF(PCA_TIME_UNIT
))
533 std::shared_ptr
<gmx::TimeUnitBehavior
> timeUnitBehavior(
534 new gmx::TimeUnitBehavior());
535 timeUnitBehavior
->setTimeUnitStore(&timeUnit
);
536 timeUnitBehavior
->setTimeUnitFromEnvironment();
537 timeUnitBehavior
->addTimeUnitOption(&options
, "tu");
538 behaviors
.addBehavior(timeUnitBehavior
);
540 if (FF(PCA_CAN_VIEW
))
543 gmx::BooleanOption("w").store(&bView
)
544 .description("View output [REF].xvg[ref], [REF].xpm[ref], "
545 "[REF].eps[ref] and [REF].pdb[ref] files"));
549 for (int i
= 0; i
< nfile
; i
++)
551 bXvgr
= bXvgr
|| (fnm
[i
].ftp
== efXVG
);
553 xvgFormat
= gmx::getDefaultXvgFormat(xvg_formats
);
557 gmx::EnumIntOption("xvg").enumValue(xvg_formats
)
559 .description("xvg plot formatting"));
562 /* Now append the program specific arguments */
563 for (int i
= 0; i
< nfile
; i
++)
565 adapter
.filenmToOptions(&options
, &fnm
[i
]);
567 for (int i
= 0; i
< npargs
; i
++)
569 adapter
.pargsToOptions(&options
, &pa
[i
]);
572 const gmx::CommandLineHelpContext
*context
=
573 gmx::GlobalCommandLineHelpContext::get();
574 if (context
!= nullptr)
576 GMX_RELEASE_ASSERT(gmx_node_rank() == 0,
577 "Help output should be handled higher up and "
578 "only get called only on the master rank");
579 gmx::CommandLineHelpWriter(options
)
580 .setHelpText(gmx::constArrayRefFromArray
<const char *>(desc
, ndesc
))
581 .setKnownIssues(gmx::constArrayRefFromArray(bugs
, nbugs
))
582 .writeHelp(*context
);
586 /* Now parse all the command-line options */
587 gmx::CommandLineParser(&options
).skipUnknown(FF(PCA_NOEXIT_ON_ARGS
))
589 behaviors
.optionsFinishing();
592 /* set program name, command line, and default values for output options */
593 output_env_init(oenv
, gmx::getProgramContext(),
594 (time_unit_t
)(timeUnit
+ 1), bView
,
595 (xvg_format_t
)(xvgFormat
+ 1), 0);
597 /* Extract Time info from arguments */
600 setTimeValue(TBEGIN
, tbegin
);
604 setTimeValue(TEND
, tend
);
608 setTimeValue(TDELTA
, tdelta
);
611 adapter
.copyValues(!FF(PCA_NOT_READ_NODE
));
615 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;