Merge branch release-5-1
[alexxy/gromacs.git] / src / gromacs / commandline / pargs.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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, 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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 #include "gmxpre.h"
38
39 #include "pargs.h"
40
41 #include <cstdlib>
42 #include <cstring>
43
44 #include <algorithm>
45 #include <list>
46
47 #include "gromacs/commandline/cmdlinehelpcontext.h"
48 #include "gromacs/commandline/cmdlinehelpwriter.h"
49 #include "gromacs/commandline/cmdlineparser.h"
50 #include "gromacs/fileio/timecontrol.h"
51 #include "gromacs/options/basicoptions.h"
52 #include "gromacs/options/filenameoption.h"
53 #include "gromacs/options/filenameoptionmanager.h"
54 #include "gromacs/options/options.h"
55 #include "gromacs/options/timeunitmanager.h"
56 #include "gromacs/utility/arrayref.h"
57 #include "gromacs/utility/basenetwork.h"
58 #include "gromacs/utility/classhelpers.h"
59 #include "gromacs/utility/cstringutil.h"
60 #include "gromacs/utility/exceptions.h"
61 #include "gromacs/utility/fatalerror.h"
62 #include "gromacs/utility/gmxassert.h"
63 #include "gromacs/utility/path.h"
64 #include "gromacs/utility/programcontext.h"
65 #include "gromacs/utility/smalloc.h"
66 #include "gromacs/utility/stringutil.h"
67
68 /* The source code in this file should be thread-safe.
69       Please keep it that way. */
70
71 int nenum(const char *const enumc[])
72 {
73     int i;
74
75     i = 1;
76     /* we *can* compare pointers directly here! */
77     while (enumc[i] && enumc[0] != enumc[i])
78     {
79         i++;
80     }
81
82     return i;
83 }
84
85 int opt2parg_int(const char *option, int nparg, t_pargs pa[])
86 {
87     int i;
88
89     for (i = 0; (i < nparg); i++)
90     {
91         if (strcmp(pa[i].option, option) == 0)
92         {
93             return *pa[i].u.i;
94         }
95     }
96
97     gmx_fatal(FARGS, "No integer option %s in pargs", option);
98
99     return 0;
100 }
101
102 gmx_bool opt2parg_bool(const char *option, int nparg, t_pargs pa[])
103 {
104     int i;
105
106     for (i = 0; (i < nparg); i++)
107     {
108         if (strcmp(pa[i].option, option) == 0)
109         {
110             return *pa[i].u.b;
111         }
112     }
113
114     gmx_fatal(FARGS, "No boolean option %s in pargs", option);
115
116     return FALSE;
117 }
118
119 real opt2parg_real(const char *option, int nparg, t_pargs pa[])
120 {
121     int i;
122
123     for (i = 0; (i < nparg); i++)
124     {
125         if (strcmp(pa[i].option, option) == 0)
126         {
127             return *pa[i].u.r;
128         }
129     }
130
131     gmx_fatal(FARGS, "No real option %s in pargs", option);
132
133     return 0.0;
134 }
135
136 const char *opt2parg_str(const char *option, int nparg, t_pargs pa[])
137 {
138     int i;
139
140     for (i = 0; (i < nparg); i++)
141     {
142         if (strcmp(pa[i].option, option) == 0)
143         {
144             return *(pa[i].u.c);
145         }
146     }
147
148     gmx_fatal(FARGS, "No string option %s in pargs", option);
149
150     return NULL;
151 }
152
153 gmx_bool opt2parg_bSet(const char *option, int nparg, t_pargs pa[])
154 {
155     int i;
156
157     for (i = 0; (i < nparg); i++)
158     {
159         if (strcmp(pa[i].option, option) == 0)
160         {
161             return pa[i].bSet;
162         }
163     }
164
165     gmx_fatal(FARGS, "No such option %s in pargs", option);
166
167     return FALSE; /* Too make some compilers happy */
168 }
169
170 const char *opt2parg_enum(const char *option, int nparg, t_pargs pa[])
171 {
172     int i;
173
174     for (i = 0; (i < nparg); i++)
175     {
176         if (strcmp(pa[i].option, option) == 0)
177         {
178             return pa[i].u.c[0];
179         }
180     }
181
182     gmx_fatal(FARGS, "No such option %s in pargs", option);
183
184     return NULL;
185 }
186
187 /********************************************************************
188  * parse_common_args()
189  */
190
191 namespace gmx
192 {
193
194 namespace
195 {
196
197 /*! \brief
198  * Returns the index of the default xvg format.
199  *
200  * \ingroup module_commandline
201  */
202 int getDefaultXvgFormat(gmx::ConstArrayRef<const char *> xvgFormats)
203 {
204     const char *const select = getenv("GMX_VIEW_XVG");
205     if (select != NULL)
206     {
207         ConstArrayRef<const char *>::const_iterator i =
208             std::find(xvgFormats.begin(), xvgFormats.end(), std::string(select));
209         if (i != xvgFormats.end())
210         {
211             return i - xvgFormats.begin();
212         }
213         else
214         {
215             return exvgNONE - 1;
216         }
217     }
218     /* The default is the first option */
219     return 0;
220 }
221
222 /*! \brief
223  * Conversion helper between t_pargs/t_filenm and Options.
224  *
225  * This class holds the necessary mapping between the old C structures and
226  * the new C++ options to allow copying values back after parsing for cases
227  * where the C++ options do not directly provide the type of value required for
228  * the C structures.
229  *
230  * \ingroup module_commandline
231  */
232 class OptionsAdapter
233 {
234     public:
235         /*! \brief
236          * Initializes the adapter to convert from a specified command line.
237          *
238          * The command line is required, because t_pargs wants to return
239          * strings by reference to the original command line.
240          * OptionsAdapter creates a copy of the `argv` array (but not the
241          * strings) to make this possible, even if the parser removes
242          * options it has recognized.
243          */
244         OptionsAdapter(int argc, const char *const argv[])
245             : argv_(argv, argv + argc)
246         {
247         }
248
249         /*! \brief
250          * Converts a t_filenm option into an Options option.
251          *
252          * \param options Options object to add the new option to.
253          * \param fnm     t_filenm option to convert.
254          */
255         void filenmToOptions(Options *options, t_filenm *fnm);
256         /*! \brief
257          * Converts a t_pargs option into an Options option.
258          *
259          * \param     options Options object to add the new option to.
260          * \param     pa      t_pargs option to convert.
261          */
262         void pargsToOptions(Options *options, t_pargs *pa);
263
264         /*! \brief
265          * Copies values back from options to t_pargs/t_filenm.
266          */
267         void copyValues(bool bReadNode);
268
269     private:
270         struct FileNameData
271         {
272             //! Creates a conversion helper for a given `t_filenm` struct.
273             explicit FileNameData(t_filenm *fnm) : fnm(fnm), optionInfo(NULL)
274             {
275             }
276
277             //! t_filenm structure to receive the final values.
278             t_filenm                 *fnm;
279             //! Option info object for the created FileNameOption.
280             FileNameOptionInfo       *optionInfo;
281             //! Value storage for the created FileNameOption.
282             std::vector<std::string>  values;
283         };
284         struct ProgramArgData
285         {
286             //! Creates a conversion helper for a given `t_pargs` struct.
287             explicit ProgramArgData(t_pargs *pa)
288                 : pa(pa), optionInfo(NULL), enumIndex(0), boolValue(false)
289             {
290             }
291
292             //! t_pargs structure to receive the final values.
293             t_pargs                 *pa;
294             //! Option info object for the created option.
295             OptionInfo              *optionInfo;
296             //! Value storage for a non-enum StringOption (unused for other types).
297             std::string              stringValue;
298             //! Value storage for an enum option (unused for other types).
299             int                      enumIndex;
300             //! Value storage for a BooleanOption (unused for other types).
301             bool                     boolValue;
302         };
303
304         std::vector<const char *>    argv_;
305         // These are lists instead of vectors to avoid relocating existing
306         // objects in case the container is reallocated (the Options object
307         // contains pointes to members of the objects, which would get
308         // invalidated).
309         std::list<FileNameData>      fileNameOptions_;
310         std::list<ProgramArgData>    programArgs_;
311
312         GMX_DISALLOW_COPY_AND_ASSIGN(OptionsAdapter);
313 };
314
315 void OptionsAdapter::filenmToOptions(Options *options, t_filenm *fnm)
316 {
317     if (fnm->opt == NULL)
318     {
319         // Existing code may use opt2fn() instead of ftp2fn() for
320         // options that use the default option name, so we need to
321         // keep the old behavior instead of fixing opt2fn().
322         // TODO: Check that this is not the case, remove this, and make
323         // opt2*() work even if fnm->opt is NULL for some options.
324         fnm->opt = ftp2defopt(fnm->ftp);
325     }
326     const bool        bRead     = ((fnm->flag & ffREAD)  != 0);
327     const bool        bWrite    = ((fnm->flag & ffWRITE) != 0);
328     const bool        bOptional = ((fnm->flag & ffOPT)   != 0);
329     const bool        bLibrary  = ((fnm->flag & ffLIB)   != 0);
330     const bool        bMultiple = ((fnm->flag & ffMULT)  != 0);
331     const bool        bMissing  = ((fnm->flag & ffALLOW_MISSING) != 0);
332     const char *const name      = &fnm->opt[1];
333     const char *      defName   = fnm->fn;
334     int               defType   = -1;
335     if (defName == NULL)
336     {
337         defName = ftp2defnm(fnm->ftp);
338     }
339     else if (Path::hasExtension(defName))
340     {
341         defType = fn2ftp(defName);
342         GMX_RELEASE_ASSERT(defType != efNR,
343                            "File name option specifies an invalid extension");
344     }
345     fileNameOptions_.push_back(FileNameData(fnm));
346     FileNameData &data = fileNameOptions_.back();
347     data.optionInfo = options->addOption(
348                 FileNameOption(name).storeVector(&data.values)
349                     .defaultBasename(defName).defaultType(defType)
350                     .legacyType(fnm->ftp).legacyOptionalBehavior()
351                     .readWriteFlags(bRead, bWrite).required(!bOptional)
352                     .libraryFile(bLibrary).multiValue(bMultiple)
353                     .allowMissing(bMissing)
354                     .description(ftp2desc(fnm->ftp)));
355 }
356
357 void OptionsAdapter::pargsToOptions(Options *options, t_pargs *pa)
358 {
359     const bool        bHidden = startsWith(pa->desc, "HIDDEN");
360     const char *const name    = &pa->option[1];
361     const char *const desc    = (bHidden ? &pa->desc[6] : pa->desc);
362     programArgs_.push_back(ProgramArgData(pa));
363     ProgramArgData   &data = programArgs_.back();
364     switch (pa->type)
365     {
366         case etINT:
367             data.optionInfo = options->addOption(
368                         IntegerOption(name).store(pa->u.i)
369                             .description(desc).hidden(bHidden));
370             return;
371         case etINT64:
372             data.optionInfo = options->addOption(
373                         Int64Option(name).store(pa->u.is)
374                             .description(desc).hidden(bHidden));
375             return;
376         case etREAL:
377             data.optionInfo = options->addOption(
378                         RealOption(name).store(pa->u.r)
379                             .description(desc).hidden(bHidden));
380             return;
381         case etTIME:
382             data.optionInfo = options->addOption(
383                         RealOption(name).store(pa->u.r).timeValue()
384                             .description(desc).hidden(bHidden));
385             return;
386         case etSTR:
387         {
388             const char *const defValue = (*pa->u.c != NULL ? *pa->u.c : "");
389             data.optionInfo = options->addOption(
390                         StringOption(name).store(&data.stringValue)
391                             .defaultValue(defValue)
392                             .description(desc).hidden(bHidden));
393             return;
394         }
395         case etBOOL:
396             data.optionInfo = options->addOption(
397                         BooleanOption(name).store(&data.boolValue)
398                             .defaultValue(*pa->u.b)
399                             .description(desc).hidden(bHidden));
400             return;
401         case etRVEC:
402             data.optionInfo = options->addOption(
403                         RealOption(name).store(*pa->u.rv).vector()
404                             .description(desc).hidden(bHidden));
405             return;
406         case etENUM:
407         {
408             const int defaultIndex = (pa->u.c[0] != NULL ? nenum(pa->u.c) - 1 : 0);
409             data.optionInfo = options->addOption(
410                         StringOption(name).storeEnumIndex(&data.enumIndex)
411                             .defaultEnumIndex(defaultIndex)
412                             .enumValueFromNullTerminatedArray(pa->u.c + 1)
413                             .description(desc).hidden(bHidden));
414             return;
415         }
416     }
417     GMX_THROW(NotImplementedError("Argument type not implemented"));
418 }
419
420 void OptionsAdapter::copyValues(bool bReadNode)
421 {
422     std::list<FileNameData>::const_iterator file;
423     for (file = fileNameOptions_.begin(); file != fileNameOptions_.end(); ++file)
424     {
425         if (!bReadNode && (file->fnm->flag & ffREAD))
426         {
427             continue;
428         }
429         if (file->optionInfo->isSet())
430         {
431             file->fnm->flag |= ffSET;
432         }
433         file->fnm->nfiles = file->values.size();
434         snew(file->fnm->fns, file->fnm->nfiles);
435         for (int i = 0; i < file->fnm->nfiles; ++i)
436         {
437             file->fnm->fns[i] = gmx_strdup(file->values[i].c_str());
438         }
439     }
440     std::list<ProgramArgData>::const_iterator arg;
441     for (arg = programArgs_.begin(); arg != programArgs_.end(); ++arg)
442     {
443         arg->pa->bSet = arg->optionInfo->isSet();
444         switch (arg->pa->type)
445         {
446             case etSTR:
447             {
448                 if (arg->pa->bSet)
449                 {
450                     std::vector<const char *>::const_iterator pos =
451                         std::find(argv_.begin(), argv_.end(), arg->stringValue);
452                     GMX_RELEASE_ASSERT(pos != argv_.end(),
453                                        "String argument got a value not in argv");
454                     *arg->pa->u.c = *pos;
455                 }
456                 break;
457             }
458             case etBOOL:
459                 *arg->pa->u.b = arg->boolValue;
460                 break;
461             case etENUM:
462                 *arg->pa->u.c = arg->pa->u.c[arg->enumIndex + 1];
463                 break;
464             default:
465                 // For other types, there is nothing type-specific to do.
466                 break;
467         }
468     }
469 }
470
471 } // namespace
472
473 } // namespace gmx
474
475 gmx_bool parse_common_args(int *argc, char *argv[], unsigned long Flags,
476                            int nfile, t_filenm fnm[], int npargs, t_pargs *pa,
477                            int ndesc, const char **desc,
478                            int nbugs, const char **bugs,
479                            output_env_t *oenv)
480 {
481     /* This array should match the order of the enum in oenv.h */
482     const char *const xvg_formats[] = { "xmgrace", "xmgr", "none" };
483
484     // Handle the flags argument, which is a bit field
485     // The FF macro returns whether or not the bit is set
486 #define FF(arg) ((Flags & arg) == arg)
487
488     try
489     {
490         double                     tbegin        = 0.0, tend = 0.0, tdelta = 0.0;
491         bool                       bBeginTimeSet = false, bEndTimeSet = false, bDtSet = false;
492         bool                       bView         = false;
493         int                        xvgFormat     = 0;
494         gmx::TimeUnitManager       timeUnitManager;
495         gmx::OptionsAdapter        adapter(*argc, argv);
496         gmx::Options               options(NULL, NULL);
497         gmx::FileNameOptionManager fileOptManager;
498
499         fileOptManager.disableInputOptionChecking(
500                 FF(PCA_NOT_READ_NODE) || FF(PCA_DISABLE_INPUT_FILE_CHECKING));
501         options.addManager(&fileOptManager);
502
503         if (FF(PCA_CAN_SET_DEFFNM))
504         {
505             fileOptManager.addDefaultFileNameOption(&options, "deffnm");
506         }
507         if (FF(PCA_CAN_BEGIN))
508         {
509             options.addOption(
510                     gmx::DoubleOption("b")
511                         .store(&tbegin).storeIsSet(&bBeginTimeSet).timeValue()
512                         .description("First frame (%t) to read from trajectory"));
513         }
514         if (FF(PCA_CAN_END))
515         {
516             options.addOption(
517                     gmx::DoubleOption("e")
518                         .store(&tend).storeIsSet(&bEndTimeSet).timeValue()
519                         .description("Last frame (%t) to read from trajectory"));
520         }
521         if (FF(PCA_CAN_DT))
522         {
523             options.addOption(
524                     gmx::DoubleOption("dt")
525                         .store(&tdelta).storeIsSet(&bDtSet).timeValue()
526                         .description("Only use frame when t MOD dt = first time (%t)"));
527         }
528         if (FF(PCA_TIME_UNIT))
529         {
530             timeUnitManager.setTimeUnitFromEnvironment();
531             timeUnitManager.addTimeUnitOption(&options, "tu");
532         }
533         if (FF(PCA_CAN_VIEW))
534         {
535             options.addOption(
536                     gmx::BooleanOption("w").store(&bView)
537                         .description("View output [REF].xvg[ref], [REF].xpm[ref], "
538                                      "[REF].eps[ref] and [REF].pdb[ref] files"));
539         }
540
541         bool bXvgr = false;
542         for (int i = 0; i < nfile; i++)
543         {
544             bXvgr = bXvgr || (fnm[i].ftp == efXVG);
545         }
546         xvgFormat = gmx::getDefaultXvgFormat(xvg_formats);
547         if (bXvgr)
548         {
549             options.addOption(
550                     gmx::StringOption("xvg").enumValue(xvg_formats)
551                         .storeEnumIndex(&xvgFormat)
552                         .description("xvg plot formatting"));
553         }
554
555         /* Now append the program specific arguments */
556         for (int i = 0; i < nfile; i++)
557         {
558             adapter.filenmToOptions(&options, &fnm[i]);
559         }
560         for (int i = 0; i < npargs; i++)
561         {
562             adapter.pargsToOptions(&options, &pa[i]);
563         }
564
565         const gmx::CommandLineHelpContext *context =
566             gmx::GlobalCommandLineHelpContext::get();
567         if (context != NULL)
568         {
569             GMX_RELEASE_ASSERT(gmx_node_rank() == 0,
570                                "Help output should be handled higher up and "
571                                "only get called only on the master rank");
572             gmx::CommandLineHelpWriter(options)
573                 .setHelpText(gmx::constArrayRefFromArray<const char *>(desc, ndesc))
574                 .setTimeUnitString(timeUnitManager.timeUnitAsString())
575                 .setKnownIssues(gmx::constArrayRefFromArray(bugs, nbugs))
576                 .writeHelp(*context);
577             return FALSE;
578         }
579
580         /* Now parse all the command-line options */
581         gmx::CommandLineParser(&options).skipUnknown(FF(PCA_NOEXIT_ON_ARGS))
582             .parse(argc, argv);
583         options.finish();
584
585         /* set program name, command line, and default values for output options */
586         output_env_init(oenv, gmx::getProgramContext(),
587                         (time_unit_t)(timeUnitManager.timeUnit() + 1), bView,
588                         (xvg_format_t)(xvgFormat + 1), 0);
589
590         timeUnitManager.scaleTimeOptions(&options);
591
592         /* Extract Time info from arguments */
593         if (bBeginTimeSet)
594         {
595             setTimeValue(TBEGIN, tbegin);
596         }
597         if (bEndTimeSet)
598         {
599             setTimeValue(TEND, tend);
600         }
601         if (bDtSet)
602         {
603             setTimeValue(TDELTA, tdelta);
604         }
605
606         adapter.copyValues(!FF(PCA_NOT_READ_NODE));
607
608         return TRUE;
609     }
610     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
611 #undef FF
612 }