Remove unnecessary config.h includes
[alexxy/gromacs.git] / src / gromacs / commandline / pargs.cpp
index 0f965c35af5e3ca6f2a0723f7cd8548d4569a87e..d0184d1a8302927112416ac263f93a298110eb2a 100644 (file)
 /*
+ * This file is part of the GROMACS molecular simulation package.
  *
- *                This source code is part of
- *
- *                 G   R   O   M   A   C   S
- *
- *          GROningen MAchine for Chemical Simulations
- *
- *                        VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
  * of the License, or (at your option) any later version.
  *
- * If you want to redistribute modifications, please consider that
- * scientific software is very special. Version control is crucial -
- * bugs must be traceable. We will be happy to consider code for
- * inclusion in the official distribution, but derived work must not
- * be called official GROMACS. Details are found in the README & COPYING
- * files - if they are missing, get the official version at www.gromacs.org.
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
  *
- * For more info, check our website at http://www.gromacs.org
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
  *
- * And Hey:
- * GROningen Mixture of Alchemy and Childrens' Stories
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
  */
-/* This file is completely threadsafe - keep it that way! */
-#include "gromacs/commandline/pargs.h"
+#include "gmxpre.h"
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "gromacs/commandline/pargs.h"
 
-#include <cctype>
-#include <cstdio>
 #include <cstdlib>
 #include <cstring>
 
-#include "gromacs/legacyheaders/gmx_fatal.h"
-#include "gromacs/legacyheaders/smalloc.h"
-#include "gromacs/legacyheaders/string2.h"
-
+#include <algorithm>
+#include <list>
+
+#include "gromacs/commandline/cmdlinehelpcontext.h"
+#include "gromacs/commandline/cmdlinehelpwriter.h"
+#include "gromacs/commandline/cmdlineparser.h"
+#include "gromacs/fileio/timecontrol.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/filenameoption.h"
+#include "gromacs/options/filenameoptionmanager.h"
+#include "gromacs/options/options.h"
+#include "gromacs/options/timeunitmanager.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basenetwork.h"
+#include "gromacs/utility/common.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/programcontext.h"
+#include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/stringutil.h"
 
 /* The source code in this file should be thread-safe.
       Please keep it that way. */
 
-static void usage(const char *type, const char *arg)
-{
-    GMX_ASSERT(arg != NULL, "NULL command-line argument should not occur");
-    gmx_fatal(FARGS, "Expected %s argument for option %s\n", type, arg);
-}
-
-/* Scan an int for argument argv[*i] from argument at argv[*i + 1].
- * eg: -p 32.  argv[*i] is only used for error reporting.
- * If there is no value, or the conversion is not successful, the
- * routine exits with an error, otherwise it returns the value found.
- * *i is incremented once.
- */
-static int iscan(int argc, char *argv[], int *i)
-{
-    const char *const arg = argv[*i];
-    if (argc <= (*i)+1)
-    {
-        usage("an integer", arg);
-    }
-    const char *const value = argv[++(*i)];
-    char             *endptr;
-    int               var = std::strtol(value, &endptr, 10);
-    if (*value == '\0' || *endptr != '\0')
-    {
-        usage("an integer", arg);
-    }
-    return var;
-}
-
-/* Same as above, but for large integer values */
-static gmx_large_int_t istepscan(int argc, char *argv[], int *i)
-{
-    const char *const arg = argv[*i];
-    if (argc <= (*i)+1)
-    {
-        usage("an integer", arg);
-    }
-    const char *const value = argv[++(*i)];
-    char             *endptr;
-    gmx_large_int_t   var = str_to_large_int_t(value, &endptr);
-    if (*value == '\0' || *endptr != '\0')
-    {
-        usage("an integer", arg);
-    }
-    return var;
-}
-
-/* Routine similar to the above, but working on doubles. */
-static double dscan(int argc, char *argv[], int *i)
-{
-    const char *const arg = argv[*i];
-    if (argc <= (*i)+1)
-    {
-        usage("a real", arg);
-    }
-    const char *const value = argv[++(*i)];
-    char             *endptr;
-    double            var = std::strtod(value, &endptr);
-    if (*value == '\0' || *endptr != '\0')
-    {
-        usage("a real", arg);
-    }
-    return var;
-}
-
-/* Routine similar to the above, but working on strings. The pointer
- * returned is a pointer to the argv field.
- */
-static char *sscan(int argc, char *argv[], int *i)
-{
-    if (argc > (*i)+1)
-    {
-        if ( (argv[(*i)+1][0] == '-') && (argc > (*i)+2) &&
-             (argv[(*i)+2][0] != '-') )
-        {
-            fprintf(stderr, "Possible missing string argument for option %s\n\n",
-                    argv[*i]);
-        }
-    }
-    else
-    {
-        usage("a string", argv[*i]);
-    }
-
-    return argv[++(*i)];
-}
-
-gmx_bool is_hidden(t_pargs *pa)
-{
-    return ((strstr(pa->desc, "HIDDEN") != NULL) ||
-            (strstr(pa->desc, "[hidden]") != NULL));
-}
-
 int nenum(const char *const enumc[])
 {
     int i;
@@ -160,138 +81,6 @@ int nenum(const char *const enumc[])
     return i;
 }
 
-void get_pargs(int *argc, char *argv[], int nparg, t_pargs pa[], gmx_bool bKeepArgs)
-{
-    int       i, j, k, match;
-    gmx_bool *bKeep;
-    char      buf[32];
-    char     *ptr;
-
-    snew(bKeep, *argc+1);
-    bKeep[0]     = TRUE;
-    bKeep[*argc] = TRUE;
-
-    for (i = 1; (i < *argc); i++)
-    {
-        bKeep[i] = TRUE;
-        for (j = 0; (j < nparg); j++)
-        {
-            if (pa[j].type == etBOOL)
-            {
-                sprintf(buf, "-no%s", pa[j].option+1);
-                if (strcmp(pa[j].option, argv[i]) == 0)
-                {
-                    *pa[j].u.b = TRUE;
-                    pa[j].bSet = TRUE;
-                    bKeep[i]   = FALSE;
-                }
-                else if (strcmp(buf, argv[i]) == 0)
-                {
-                    *pa[j].u.b = FALSE;
-                    pa[j].bSet = TRUE;
-                    bKeep[i]   = FALSE;
-                }
-            }
-            else if (strcmp(pa[j].option, argv[i]) == 0)
-            {
-                if (pa[j].bSet)
-                {
-                    fprintf(stderr, "Setting option %s more than once!\n",
-                            pa[j].option);
-                }
-                pa[j].bSet = TRUE;
-                bKeep[i]   = FALSE;
-                switch (pa[j].type)
-                {
-                    case etINT:
-                        *pa[j].u.i = iscan(*argc, argv, &i);
-                        break;
-                    case etGMX_LARGE_INT:
-                        *pa[j].u.is = istepscan(*argc, argv, &i);
-                        break;
-                    case etTIME:
-                    case etREAL:
-                        *pa[j].u.r = dscan(*argc, argv, &i);
-                        break;
-                    case etSTR:
-                        *(pa[j].u.c) = sscan(*argc, argv, &i);
-                        break;
-                    case etENUM:
-                        match = -1;
-                        ptr   = sscan(*argc, argv, &i);
-                        for (k = 1; (pa[j].u.c[k] != NULL); k++)
-                        {
-                            /* only check ptr against beginning of
-                               pa[j].u.c[k] */
-                            if (gmx_strncasecmp(ptr, pa[j].u.c[k], strlen(ptr)) == 0)
-                            {
-                                if ( ( match == -1 ) ||
-                                     ( strlen(pa[j].u.c[k]) <
-                                       strlen(pa[j].u.c[match]) ) )
-                                {
-                                    match = k;
-                                }
-                            }
-                        }
-                        if (match != -1)
-                        {
-                            pa[j].u.c[0] = pa[j].u.c[match];
-                        }
-                        else
-                        {
-                            gmx_fatal(FARGS, "Invalid argument %s for option %s",
-                                      ptr, pa[j].option);
-                        }
-                        break;
-                    case etRVEC:
-                        (*pa[j].u.rv)[0] = dscan(*argc, argv, &i);
-                        if ( (i+1 == *argc) ||
-                             ( (argv[i+1][0] == '-') &&
-                               !isdigit(argv[i+1][1]) ) )
-                        {
-                            (*pa[j].u.rv)[1]     =
-                                (*pa[j].u.rv)[2] =
-                                    (*pa[j].u.rv)[0];
-                        }
-                        else
-                        {
-                            bKeep[i]         = FALSE;
-                            (*pa[j].u.rv)[1] = dscan(*argc, argv, &i);
-                            if ( (i+1 == *argc) ||
-                                 ( (argv[i+1][0] == '-') &&
-                                   !isdigit(argv[i+1][1]) ) )
-                            {
-                                gmx_fatal(FARGS,
-                                          "%s: vector must have 1 or 3 real parameters",
-                                          pa[j].option);
-                            }
-                            bKeep[i]         = FALSE;
-                            (*pa[j].u.rv)[2] = dscan(*argc, argv, &i);
-                        }
-                        break;
-                    default:
-                        gmx_fatal(FARGS, "Invalid type %d in pargs", pa[j].type);
-                }
-                /* i may be incremented, so set it to not keep */
-                bKeep[i] = FALSE;
-            }
-        }
-    }
-    if (!bKeepArgs)
-    {
-        /* Remove used entries */
-        for (i = j = 0; (i <= *argc); i++)
-        {
-            if (bKeep[i])
-            {
-                argv[j++] = argv[i];
-            }
-        }
-        (*argc) = j-1;
-    }
-    sfree(bKeep);
-}
-
 int opt2parg_int(const char *option, int nparg, t_pargs pa[])
 {
     int i;
@@ -309,7 +98,7 @@ int opt2parg_int(const char *option, int nparg, t_pargs pa[])
     return 0;
 }
 
-gmx_bool opt2parg_gmx_bool(const char *option, int nparg, t_pargs pa[])
+gmx_bool opt2parg_bool(const char *option, int nparg, t_pargs pa[])
 {
     int i;
 
@@ -393,3 +182,419 @@ const char *opt2parg_enum(const char *option, int nparg, t_pargs pa[])
 
     return NULL;
 }
+
+/********************************************************************
+ * parse_common_args()
+ */
+
+namespace gmx
+{
+
+namespace
+{
+
+/*! \brief
+ * Returns the index of the default xvg format.
+ *
+ * \ingroup module_commandline
+ */
+int getDefaultXvgFormat(gmx::ConstArrayRef<const char *> xvgFormats)
+{
+    const char *const select = getenv("GMX_VIEW_XVG");
+    if (select != NULL)
+    {
+        ConstArrayRef<const char *>::const_iterator i =
+            std::find(xvgFormats.begin(), xvgFormats.end(), std::string(select));
+        if (i != xvgFormats.end())
+        {
+            return i - xvgFormats.begin();
+        }
+        else
+        {
+            return exvgNONE - 1;
+        }
+    }
+    /* The default is the first option */
+    return 0;
+}
+
+/*! \brief
+ * Conversion helper between t_pargs/t_filenm and Options.
+ *
+ * This class holds the necessary mapping between the old C structures and
+ * the new C++ options to allow copying values back after parsing for cases
+ * where the C++ options do not directly provide the type of value required for
+ * the C structures.
+ *
+ * \ingroup module_commandline
+ */
+class OptionsAdapter
+{
+    public:
+        /*! \brief
+         * Initializes the adapter to convert from a specified command line.
+         *
+         * The command line is required, because t_pargs wants to return
+         * strings by reference to the original command line.
+         * OptionsAdapter creates a copy of the `argv` array (but not the
+         * strings) to make this possible, even if the parser removes
+         * options it has recognized.
+         */
+        OptionsAdapter(int argc, const char *const argv[])
+            : argv_(argv, argv + argc)
+        {
+        }
+
+        /*! \brief
+         * Converts a t_filenm option into an Options option.
+         *
+         * \param options Options object to add the new option to.
+         * \param fnm     t_filenm option to convert.
+         */
+        void filenmToOptions(Options *options, t_filenm *fnm);
+        /*! \brief
+         * Converts a t_pargs option into an Options option.
+         *
+         * \param     options Options object to add the new option to.
+         * \param     pa      t_pargs option to convert.
+         */
+        void pargsToOptions(Options *options, t_pargs *pa);
+
+        /*! \brief
+         * Copies values back from options to t_pargs/t_filenm.
+         */
+        void copyValues(bool bReadNode);
+
+    private:
+        struct FileNameData
+        {
+            //! Creates a conversion helper for a given `t_filenm` struct.
+            explicit FileNameData(t_filenm *fnm) : fnm(fnm), optionInfo(NULL)
+            {
+            }
+
+            //! t_filenm structure to receive the final values.
+            t_filenm                 *fnm;
+            //! Option info object for the created FileNameOption.
+            FileNameOptionInfo       *optionInfo;
+            //! Value storage for the created FileNameOption.
+            std::vector<std::string>  values;
+        };
+        struct ProgramArgData
+        {
+            //! Creates a conversion helper for a given `t_pargs` struct.
+            explicit ProgramArgData(t_pargs *pa)
+                : pa(pa), optionInfo(NULL), enumIndex(0), boolValue(false)
+            {
+            }
+
+            //! t_pargs structure to receive the final values.
+            t_pargs                 *pa;
+            //! Option info object for the created option.
+            OptionInfo              *optionInfo;
+            //! Value storage for a non-enum StringOption (unused for other types).
+            std::string              stringValue;
+            //! Value storage for an enum option (unused for other types).
+            int                      enumIndex;
+            //! Value storage for a BooleanOption (unused for other types).
+            bool                     boolValue;
+        };
+
+        std::vector<const char *>    argv_;
+        // These are lists instead of vectors to avoid relocating existing
+        // objects in case the container is reallocated (the Options object
+        // contains pointes to members of the objects, which would get
+        // invalidated).
+        std::list<FileNameData>      fileNameOptions_;
+        std::list<ProgramArgData>    programArgs_;
+
+        GMX_DISALLOW_COPY_AND_ASSIGN(OptionsAdapter);
+};
+
+void OptionsAdapter::filenmToOptions(Options *options, t_filenm *fnm)
+{
+    if (fnm->opt == NULL)
+    {
+        // Existing code may use opt2fn() instead of ftp2fn() for
+        // options that use the default option name, so we need to
+        // keep the old behavior instead of fixing opt2fn().
+        // TODO: Check that this is not the case, remove this, and make
+        // opt2*() work even if fnm->opt is NULL for some options.
+        fnm->opt = ftp2defopt(fnm->ftp);
+    }
+    const bool        bRead     = ((fnm->flag & ffREAD)  != 0);
+    const bool        bWrite    = ((fnm->flag & ffWRITE) != 0);
+    const bool        bOptional = ((fnm->flag & ffOPT)   != 0);
+    const bool        bLibrary  = ((fnm->flag & ffLIB)   != 0);
+    const bool        bMultiple = ((fnm->flag & ffMULT)  != 0);
+    const char *const name      = &fnm->opt[1];
+    const char *      defName   = fnm->fn;
+    if (defName == NULL)
+    {
+        defName = ftp2defnm(fnm->ftp);
+    }
+    fileNameOptions_.push_back(FileNameData(fnm));
+    FileNameData &data = fileNameOptions_.back();
+    data.optionInfo = options->addOption(
+                FileNameOption(name).storeVector(&data.values)
+                    .defaultBasename(defName).legacyType(fnm->ftp)
+                    .legacyOptionalBehavior()
+                    .readWriteFlags(bRead, bWrite).required(!bOptional)
+                    .libraryFile(bLibrary).multiValue(bMultiple)
+                    .description(ftp2desc(fnm->ftp)));
+}
+
+void OptionsAdapter::pargsToOptions(Options *options, t_pargs *pa)
+{
+    const bool        bHidden = startsWith(pa->desc, "HIDDEN");
+    const char *const name    = &pa->option[1];
+    const char *const desc    = (bHidden ? &pa->desc[6] : pa->desc);
+    programArgs_.push_back(ProgramArgData(pa));
+    ProgramArgData   &data = programArgs_.back();
+    switch (pa->type)
+    {
+        case etINT:
+            data.optionInfo = options->addOption(
+                        IntegerOption(name).store(pa->u.i)
+                            .description(desc).hidden(bHidden));
+            return;
+        case etINT64:
+            data.optionInfo = options->addOption(
+                        Int64Option(name).store(pa->u.is)
+                            .description(desc).hidden(bHidden));
+            return;
+        case etREAL:
+            data.optionInfo = options->addOption(
+                        RealOption(name).store(pa->u.r)
+                            .description(desc).hidden(bHidden));
+            return;
+        case etTIME:
+            data.optionInfo = options->addOption(
+                        RealOption(name).store(pa->u.r).timeValue()
+                            .description(desc).hidden(bHidden));
+            return;
+        case etSTR:
+        {
+            const char *const defValue = (*pa->u.c != NULL ? *pa->u.c : "");
+            data.optionInfo = options->addOption(
+                        StringOption(name).store(&data.stringValue)
+                            .defaultValue(defValue)
+                            .description(desc).hidden(bHidden));
+            return;
+        }
+        case etBOOL:
+            data.optionInfo = options->addOption(
+                        BooleanOption(name).store(&data.boolValue)
+                            .defaultValue(*pa->u.b)
+                            .description(desc).hidden(bHidden));
+            return;
+        case etRVEC:
+            data.optionInfo = options->addOption(
+                        RealOption(name).store(*pa->u.rv).vector()
+                            .description(desc).hidden(bHidden));
+            return;
+        case etENUM:
+        {
+            const int defaultIndex = (pa->u.c[0] != NULL ? nenum(pa->u.c) - 1 : 0);
+            data.optionInfo = options->addOption(
+                        StringOption(name).storeEnumIndex(&data.enumIndex)
+                            .defaultEnumIndex(defaultIndex)
+                            .enumValueFromNullTerminatedArray(pa->u.c + 1)
+                            .description(desc).hidden(bHidden));
+            return;
+        }
+    }
+    GMX_THROW(NotImplementedError("Argument type not implemented"));
+}
+
+void OptionsAdapter::copyValues(bool bReadNode)
+{
+    std::list<FileNameData>::const_iterator file;
+    for (file = fileNameOptions_.begin(); file != fileNameOptions_.end(); ++file)
+    {
+        if (!bReadNode && (file->fnm->flag & ffREAD))
+        {
+            continue;
+        }
+        if (file->optionInfo->isSet())
+        {
+            file->fnm->flag |= ffSET;
+        }
+        file->fnm->nfiles = file->values.size();
+        snew(file->fnm->fns, file->fnm->nfiles);
+        for (int i = 0; i < file->fnm->nfiles; ++i)
+        {
+            file->fnm->fns[i] = gmx_strdup(file->values[i].c_str());
+        }
+    }
+    std::list<ProgramArgData>::const_iterator arg;
+    for (arg = programArgs_.begin(); arg != programArgs_.end(); ++arg)
+    {
+        arg->pa->bSet = arg->optionInfo->isSet();
+        switch (arg->pa->type)
+        {
+            case etSTR:
+            {
+                if (arg->pa->bSet)
+                {
+                    std::vector<const char *>::const_iterator pos =
+                        std::find(argv_.begin(), argv_.end(), arg->stringValue);
+                    GMX_RELEASE_ASSERT(pos != argv_.end(),
+                                       "String argument got a value not in argv");
+                    *arg->pa->u.c = *pos;
+                }
+                break;
+            }
+            case etBOOL:
+                *arg->pa->u.b = arg->boolValue;
+                break;
+            case etENUM:
+                *arg->pa->u.c = arg->pa->u.c[arg->enumIndex + 1];
+                break;
+            default:
+                // For other types, there is nothing type-specific to do.
+                break;
+        }
+    }
+}
+
+} // namespace
+
+} // namespace gmx
+
+gmx_bool parse_common_args(int *argc, char *argv[], unsigned long Flags,
+                           int nfile, t_filenm fnm[], int npargs, t_pargs *pa,
+                           int ndesc, const char **desc,
+                           int nbugs, const char **bugs,
+                           output_env_t *oenv)
+{
+    /* This array should match the order of the enum in oenv.h */
+    const char *const xvg_formats[] = { "xmgrace", "xmgr", "none" };
+
+    // Handle the flags argument, which is a bit field
+    // The FF macro returns whether or not the bit is set
+#define FF(arg) ((Flags & arg) == arg)
+
+    try
+    {
+        double                     tbegin    = 0.0, tend = 0.0, tdelta = 0.0;
+        bool                       bView     = false;
+        int                        xvgFormat = 0;
+        gmx::TimeUnitManager       timeUnitManager;
+        gmx::OptionsAdapter        adapter(*argc, argv);
+        gmx::Options               options(NULL, NULL);
+        gmx::FileNameOptionManager fileOptManager;
+
+        fileOptManager.disableInputOptionChecking(
+                FF(PCA_NOT_READ_NODE) || FF(PCA_DISABLE_INPUT_FILE_CHECKING));
+        options.addManager(&fileOptManager);
+        options.setDescription(gmx::constArrayRefFromArray<const char *>(desc, ndesc));
+
+        if (FF(PCA_CAN_SET_DEFFNM))
+        {
+            fileOptManager.addDefaultFileNameOption(&options, "deffnm");
+        }
+        if (FF(PCA_CAN_BEGIN))
+        {
+            options.addOption(
+                    gmx::DoubleOption("b").store(&tbegin).timeValue()
+                        .description("First frame (%t) to read from trajectory"));
+        }
+        if (FF(PCA_CAN_END))
+        {
+            options.addOption(
+                    gmx::DoubleOption("e").store(&tend).timeValue()
+                        .description("Last frame (%t) to read from trajectory"));
+        }
+        if (FF(PCA_CAN_DT))
+        {
+            options.addOption(
+                    gmx::DoubleOption("dt").store(&tdelta).timeValue()
+                        .description("Only use frame when t MOD dt = first time (%t)"));
+        }
+        if (FF(PCA_TIME_UNIT))
+        {
+            timeUnitManager.setTimeUnitFromEnvironment();
+            timeUnitManager.addTimeUnitOption(&options, "tu");
+        }
+        if (FF(PCA_CAN_VIEW))
+        {
+            options.addOption(
+                    gmx::BooleanOption("w").store(&bView)
+                        .description("View output [TT].xvg[tt], [TT].xpm[tt], "
+                                     "[TT].eps[tt] and [TT].pdb[tt] files"));
+        }
+
+        bool bXvgr = false;
+        for (int i = 0; i < nfile; i++)
+        {
+            bXvgr = bXvgr || (fnm[i].ftp == efXVG);
+        }
+        xvgFormat = gmx::getDefaultXvgFormat(xvg_formats);
+        if (bXvgr)
+        {
+            options.addOption(
+                    gmx::StringOption("xvg").enumValue(xvg_formats)
+                        .storeEnumIndex(&xvgFormat)
+                        .description("xvg plot formatting"));
+        }
+
+        /* Now append the program specific arguments */
+        for (int i = 0; i < nfile; i++)
+        {
+            adapter.filenmToOptions(&options, &fnm[i]);
+        }
+        for (int i = 0; i < npargs; i++)
+        {
+            adapter.pargsToOptions(&options, &pa[i]);
+        }
+
+        const gmx::CommandLineHelpContext *context =
+            gmx::GlobalCommandLineHelpContext::get();
+        if (context != NULL)
+        {
+            GMX_RELEASE_ASSERT(gmx_node_rank() == 0,
+                               "Help output should be handled higher up and "
+                               "only get called only on the master rank");
+            gmx::CommandLineHelpWriter(options)
+                .setShowDescriptions(true)
+                .setTimeUnitString(timeUnitManager.timeUnitAsString())
+                .setKnownIssues(gmx::constArrayRefFromArray(bugs, nbugs))
+                .writeHelp(*context);
+            return FALSE;
+        }
+
+        /* Now parse all the command-line options */
+        gmx::CommandLineParser(&options).skipUnknown(FF(PCA_NOEXIT_ON_ARGS))
+            .parse(argc, argv);
+        options.finish();
+
+        /* set program name, command line, and default values for output options */
+        output_env_init(oenv, gmx::getProgramContext(),
+                        (time_unit_t)(timeUnitManager.timeUnit() + 1), bView,
+                        (xvg_format_t)(xvgFormat + 1), 0);
+
+        timeUnitManager.scaleTimeOptions(&options);
+
+        /* Extract Time info from arguments */
+        // TODO: Use OptionInfo objects instead of string constants
+        if (FF(PCA_CAN_BEGIN) && options.isSet("b"))
+        {
+            setTimeValue(TBEGIN, tbegin);
+        }
+        if (FF(PCA_CAN_END) && options.isSet("e"))
+        {
+            setTimeValue(TEND, tend);
+        }
+        if (FF(PCA_CAN_DT) && options.isSet("dt"))
+        {
+            setTimeValue(TDELTA, tdelta);
+        }
+
+        adapter.copyValues(!FF(PCA_NOT_READ_NODE));
+
+        return TRUE;
+    }
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
+#undef FF
+}