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, 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 /* This file is completely threadsafe - keep it that way! */
38 #include "gromacs/commandline/pargs.h"
56 #include "thread_mpi/threads.h"
58 #include "gromacs/commandline/cmdlinehelpcontext.h"
59 #include "gromacs/commandline/cmdlinehelpwriter.h"
60 #include "gromacs/commandline/cmdlineparser.h"
61 #include "gromacs/fileio/timecontrol.h"
62 #include "gromacs/options/basicoptions.h"
63 #include "gromacs/options/filenameoption.h"
64 #include "gromacs/options/filenameoptionmanager.h"
65 #include "gromacs/options/options.h"
66 #include "gromacs/options/timeunitmanager.h"
67 #include "gromacs/utility/arrayref.h"
68 #include "gromacs/utility/basenetwork.h"
69 #include "gromacs/utility/common.h"
70 #include "gromacs/utility/cstringutil.h"
71 #include "gromacs/utility/exceptions.h"
72 #include "gromacs/utility/fatalerror.h"
73 #include "gromacs/utility/gmxassert.h"
74 #include "gromacs/utility/programcontext.h"
75 #include "gromacs/utility/smalloc.h"
76 #include "gromacs/utility/stringutil.h"
78 /* The source code in this file should be thread-safe.
79 Please keep it that way. */
81 int nenum(const char *const enumc[])
86 /* we *can* compare pointers directly here! */
87 while (enumc[i] && enumc[0] != enumc[i])
95 int opt2parg_int(const char *option, int nparg, t_pargs pa[])
99 for (i = 0; (i < nparg); i++)
101 if (strcmp(pa[i].option, option) == 0)
107 gmx_fatal(FARGS, "No integer option %s in pargs", option);
112 gmx_bool opt2parg_bool(const char *option, int nparg, t_pargs pa[])
116 for (i = 0; (i < nparg); i++)
118 if (strcmp(pa[i].option, option) == 0)
124 gmx_fatal(FARGS, "No boolean option %s in pargs", option);
129 real opt2parg_real(const char *option, int nparg, t_pargs pa[])
133 for (i = 0; (i < nparg); i++)
135 if (strcmp(pa[i].option, option) == 0)
141 gmx_fatal(FARGS, "No real option %s in pargs", option);
146 const char *opt2parg_str(const char *option, int nparg, t_pargs pa[])
150 for (i = 0; (i < nparg); i++)
152 if (strcmp(pa[i].option, option) == 0)
158 gmx_fatal(FARGS, "No string option %s in pargs", option);
163 gmx_bool opt2parg_bSet(const char *option, int nparg, t_pargs pa[])
167 for (i = 0; (i < nparg); i++)
169 if (strcmp(pa[i].option, option) == 0)
175 gmx_fatal(FARGS, "No such option %s in pargs", option);
177 return FALSE; /* Too make some compilers happy */
180 const char *opt2parg_enum(const char *option, int nparg, t_pargs pa[])
184 for (i = 0; (i < nparg); i++)
186 if (strcmp(pa[i].option, option) == 0)
192 gmx_fatal(FARGS, "No such option %s in pargs", option);
197 /********************************************************************
198 * parse_common_args()
208 * Returns the index of the default xvg format.
210 * \ingroup module_commandline
212 int getDefaultXvgFormat(gmx::ConstArrayRef<const char *> xvgFormats)
214 const char *const select = getenv("GMX_VIEW_XVG");
217 ConstArrayRef<const char *>::const_iterator i =
218 std::find(xvgFormats.begin(), xvgFormats.end(), std::string(select));
219 if (i != xvgFormats.end())
221 return i - xvgFormats.begin();
228 /* The default is the first option */
233 * Conversion helper between t_pargs/t_filenm and Options.
235 * This class holds the necessary mapping between the old C structures and
236 * the new C++ options to allow copying values back after parsing for cases
237 * where the C++ options do not directly provide the type of value required for
240 * \ingroup module_commandline
246 * Initializes the adapter to convert from a specified command line.
248 * The command line is required, because t_pargs wants to return
249 * strings by reference to the original command line.
250 * OptionsAdapter creates a copy of the `argv` array (but not the
251 * strings) to make this possible, even if the parser removes
252 * options it has recognized.
254 OptionsAdapter(int argc, const char *const argv[])
255 : argv_(argv, argv + argc)
260 * Converts a t_filenm option into an Options option.
262 * \param options Options object to add the new option to.
263 * \param fnm t_filenm option to convert.
265 void filenmToOptions(Options *options, t_filenm *fnm);
267 * Converts a t_pargs option into an Options option.
269 * \param options Options object to add the new option to.
270 * \param pa t_pargs option to convert.
272 void pargsToOptions(Options *options, t_pargs *pa);
275 * Copies values back from options to t_pargs/t_filenm.
277 void copyValues(bool bReadNode);
282 //! Creates a conversion helper for a given `t_filenm` struct.
283 explicit FileNameData(t_filenm *fnm) : fnm(fnm), optionInfo(NULL)
287 //! t_filenm structure to receive the final values.
289 //! Option info object for the created FileNameOption.
290 FileNameOptionInfo *optionInfo;
291 //! Value storage for the created FileNameOption.
292 std::vector<std::string> values;
294 struct ProgramArgData
296 //! Creates a conversion helper for a given `t_pargs` struct.
297 explicit ProgramArgData(t_pargs *pa)
298 : pa(pa), optionInfo(NULL), enumIndex(0), boolValue(false)
302 //! t_pargs structure to receive the final values.
304 //! Option info object for the created option.
305 OptionInfo *optionInfo;
306 //! Value storage for a non-enum StringOption (unused for other types).
307 std::string stringValue;
308 //! Value storage for an enum option (unused for other types).
310 //! Value storage for a BooleanOption (unused for other types).
314 std::vector<const char *> argv_;
315 // These are lists instead of vectors to avoid relocating existing
316 // objects in case the container is reallocated (the Options object
317 // contains pointes to members of the objects, which would get
319 std::list<FileNameData> fileNameOptions_;
320 std::list<ProgramArgData> programArgs_;
322 GMX_DISALLOW_COPY_AND_ASSIGN(OptionsAdapter);
325 void OptionsAdapter::filenmToOptions(Options *options, t_filenm *fnm)
327 if (fnm->opt == NULL)
329 // Existing code may use opt2fn() instead of ftp2fn() for
330 // options that use the default option name, so we need to
331 // keep the old behavior instead of fixing opt2fn().
332 // TODO: Check that this is not the case, remove this, and make
333 // opt2*() work even if fnm->opt is NULL for some options.
334 fnm->opt = ftp2defopt(fnm->ftp);
336 const bool bRead = ((fnm->flag & ffREAD) != 0);
337 const bool bWrite = ((fnm->flag & ffWRITE) != 0);
338 const bool bOptional = ((fnm->flag & ffOPT) != 0);
339 const bool bLibrary = ((fnm->flag & ffLIB) != 0);
340 const bool bMultiple = ((fnm->flag & ffMULT) != 0);
341 const char *const name = &fnm->opt[1];
342 const char * defName = fnm->fn;
345 defName = ftp2defnm(fnm->ftp);
347 fileNameOptions_.push_back(FileNameData(fnm));
348 FileNameData &data = fileNameOptions_.back();
349 data.optionInfo = options->addOption(
350 FileNameOption(name).storeVector(&data.values)
351 .defaultBasename(defName).legacyType(fnm->ftp)
352 .legacyOptionalBehavior()
353 .readWriteFlags(bRead, bWrite).required(!bOptional)
354 .libraryFile(bLibrary).multiValue(bMultiple)
355 .description(ftp2desc(fnm->ftp)));
358 void OptionsAdapter::pargsToOptions(Options *options, t_pargs *pa)
360 const bool bHidden = startsWith(pa->desc, "HIDDEN");
361 const char *const name = &pa->option[1];
362 const char *const desc = (bHidden ? &pa->desc[6] : pa->desc);
363 programArgs_.push_back(ProgramArgData(pa));
364 ProgramArgData &data = programArgs_.back();
368 data.optionInfo = options->addOption(
369 IntegerOption(name).store(pa->u.i)
370 .description(desc).hidden(bHidden));
373 data.optionInfo = options->addOption(
374 Int64Option(name).store(pa->u.is)
375 .description(desc).hidden(bHidden));
378 data.optionInfo = options->addOption(
379 RealOption(name).store(pa->u.r)
380 .description(desc).hidden(bHidden));
383 data.optionInfo = options->addOption(
384 RealOption(name).store(pa->u.r).timeValue()
385 .description(desc).hidden(bHidden));
389 const char *const defValue = (*pa->u.c != NULL ? *pa->u.c : "");
390 data.optionInfo = options->addOption(
391 StringOption(name).store(&data.stringValue)
392 .defaultValue(defValue)
393 .description(desc).hidden(bHidden));
397 data.optionInfo = options->addOption(
398 BooleanOption(name).store(&data.boolValue)
399 .defaultValue(*pa->u.b)
400 .description(desc).hidden(bHidden));
403 data.optionInfo = options->addOption(
404 RealOption(name).store(*pa->u.rv).vector()
405 .description(desc).hidden(bHidden));
409 const int defaultIndex = (pa->u.c[0] != NULL ? nenum(pa->u.c) - 1 : 0);
410 data.optionInfo = options->addOption(
411 StringOption(name).storeEnumIndex(&data.enumIndex)
412 .defaultEnumIndex(defaultIndex)
413 .enumValueFromNullTerminatedArray(pa->u.c + 1)
414 .description(desc).hidden(bHidden));
418 GMX_THROW(NotImplementedError("Argument type not implemented"));
421 void OptionsAdapter::copyValues(bool bReadNode)
423 std::list<FileNameData>::const_iterator file;
424 for (file = fileNameOptions_.begin(); file != fileNameOptions_.end(); ++file)
426 // FIXME: FF_NOT_READ_NODE should also skip all fexist() calls in
427 // FileNameOption. However, it is not currently used, and other
428 // commented out code for using it is also outdated, so left this for
430 if (!bReadNode && (file->fnm->flag & ffREAD))
434 if (file->optionInfo->isSet())
436 file->fnm->flag |= ffSET;
438 file->fnm->nfiles = file->values.size();
439 snew(file->fnm->fns, file->fnm->nfiles);
440 for (int i = 0; i < file->fnm->nfiles; ++i)
442 // TODO: Check for out-of-memory.
443 file->fnm->fns[i] = strdup(file->values[i].c_str());
446 std::list<ProgramArgData>::const_iterator arg;
447 for (arg = programArgs_.begin(); arg != programArgs_.end(); ++arg)
449 arg->pa->bSet = arg->optionInfo->isSet();
450 switch (arg->pa->type)
456 std::vector<const char *>::const_iterator pos =
457 std::find(argv_.begin(), argv_.end(), arg->stringValue);
458 GMX_RELEASE_ASSERT(pos != argv_.end(),
459 "String argument got a value not in argv");
460 *arg->pa->u.c = *pos;
465 *arg->pa->u.b = arg->boolValue;
468 *arg->pa->u.c = arg->pa->u.c[arg->enumIndex + 1];
471 // For other types, there is nothing type-specific to do.
481 gmx_bool parse_common_args(int *argc, char *argv[], unsigned long Flags,
482 int nfile, t_filenm fnm[], int npargs, t_pargs *pa,
483 int ndesc, const char **desc,
484 int nbugs, const char **bugs,
487 /* This array should match the order of the enum in oenv.h */
488 const char *const xvg_formats[] = { "xmgrace", "xmgr", "none" };
490 // Handle the flags argument, which is a bit field
491 // The FF macro returns whether or not the bit is set
492 #define FF(arg) ((Flags & arg) == arg)
496 int nicelevel = 0, debug_level = 0;
497 double tbegin = 0.0, tend = 0.0, tdelta = 0.0;
500 gmx::TimeUnitManager timeUnitManager;
501 gmx::OptionsAdapter adapter(*argc, argv);
502 gmx::Options options(NULL, NULL);
503 gmx::FileNameOptionManager fileOptManager;
505 options.setDescription(gmx::ConstArrayRef<const char *>(desc, ndesc));
507 gmx::IntegerOption("debug").store(&debug_level).hidden()
508 .description("Write file with debug information, "
509 "1: short, 2: also x and f"));
512 gmx::IntegerOption("nice").store(&nicelevel)
513 .defaultValue(FF(PCA_BE_NICE) ? 19 : 0)
514 .description("Set the nicelevel"));
516 if (FF(PCA_CAN_SET_DEFFNM))
518 fileOptManager.addDefaultFileNameOption(&options, "deffnm");
520 if (FF(PCA_CAN_BEGIN))
523 gmx::DoubleOption("b").store(&tbegin).timeValue()
524 .description("First frame (%t) to read from trajectory"));
529 gmx::DoubleOption("e").store(&tend).timeValue()
530 .description("Last frame (%t) to read from trajectory"));
535 gmx::DoubleOption("dt").store(&tdelta).timeValue()
536 .description("Only use frame when t MOD dt = first time (%t)"));
538 if (FF(PCA_TIME_UNIT))
540 timeUnitManager.setTimeUnitFromEnvironment();
541 timeUnitManager.addTimeUnitOption(&options, "tu");
543 if (FF(PCA_CAN_VIEW))
546 gmx::BooleanOption("w").store(&bView)
547 .description("View output [TT].xvg[tt], [TT].xpm[tt], "
548 "[TT].eps[tt] and [TT].pdb[tt] files"));
552 for (int i = 0; i < nfile; i++)
554 bXvgr = bXvgr || (fnm[i].ftp == efXVG);
556 xvgFormat = gmx::getDefaultXvgFormat(xvg_formats);
560 gmx::StringOption("xvg").enumValue(xvg_formats)
561 .storeEnumIndex(&xvgFormat)
562 .description("xvg plot formatting"));
565 /* Now append the program specific arguments */
566 for (int i = 0; i < nfile; i++)
568 adapter.filenmToOptions(&options, &fnm[i]);
570 for (int i = 0; i < npargs; i++)
572 adapter.pargsToOptions(&options, &pa[i]);
575 setManagerForFileNameOptions(&options, &fileOptManager);
577 const gmx::CommandLineHelpContext *context =
578 gmx::GlobalCommandLineHelpContext::get();
581 if (!(FF(PCA_QUIET)))
583 gmx::CommandLineHelpWriter(options)
584 .setShowDescriptions(true)
585 .setTimeUnitString(timeUnitManager.timeUnitAsString())
586 .setKnownIssues(gmx::ConstArrayRef<const char *>(bugs, nbugs))
587 .writeHelp(*context);
592 /* Now parse all the command-line options */
593 gmx::CommandLineParser(&options).skipUnknown(FF(PCA_NOEXIT_ON_ARGS))
597 /* set program name, command line, and default values for output options */
598 output_env_init(oenv, gmx::getProgramContext(),
599 (time_unit_t)(timeUnitManager.timeUnit() + 1), bView,
600 (xvg_format_t)(xvgFormat + 1), 0, debug_level);
602 /* Open the debug file */
607 if (gmx_mpi_initialized())
609 sprintf(buf, "%s%d.debug", output_env_get_short_program_name(*oenv),
614 sprintf(buf, "%s.debug", output_env_get_short_program_name(*oenv));
617 init_debug(debug_level, buf);
618 fprintf(stderr, "Opening debug file %s (src code file %s, line %d)\n",
619 buf, __FILE__, __LINE__);
622 /* Set the nice level */
625 /* The some system, e.g. the catamount kernel on cray xt3 do not have nice(2). */
628 static gmx_bool nice_set = FALSE; /* only set it once */
629 static tMPI_Thread_mutex_t init_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
630 tMPI_Thread_mutex_lock(&init_mutex);
633 if (nice(nicelevel) == -1)
635 /* Do nothing, but use the return value to avoid warnings. */
639 tMPI_Thread_mutex_unlock(&init_mutex);
644 timeUnitManager.scaleTimeOptions(&options);
646 /* Extract Time info from arguments */
647 // TODO: Use OptionInfo objects instead of string constants
648 if (FF(PCA_CAN_BEGIN) && options.isSet("b"))
650 setTimeValue(TBEGIN, tbegin);
652 if (FF(PCA_CAN_END) && options.isSet("-e"))
654 setTimeValue(TEND, tend);
656 if (FF(PCA_CAN_DT) && options.isSet("-dt"))
658 setTimeValue(TDELTA, tdelta);
661 adapter.copyValues(!FF(PCA_NOT_READ_NODE));
665 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;