Remove unnecessary config.h includes
[alexxy/gromacs.git] / src / gromacs / commandline / pargs.cpp
index 5fab063d6808db46d4dd03eed69a29213a09bf7d..d0184d1a8302927112416ac263f93a298110eb2a 100644 (file)
  * 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>
 
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-/* used for npri */
-#ifdef __sgi
-#include <sys/schedctl.h>
-#include <sys/sysmp.h>
-#endif
-
-#include "gromacs/legacyheaders/gmx_fatal.h"
-#include "gromacs/legacyheaders/macros.h"
-#include "gromacs/legacyheaders/network.h"
-#include "gromacs/legacyheaders/smalloc.h"
-#include "gromacs/legacyheaders/string2.h"
-#include "gromacs/legacyheaders/thread_mpi/threads.h"
+#include <algorithm>
+#include <list>
 
 #include "gromacs/commandline/cmdlinehelpcontext.h"
 #include "gromacs/commandline/cmdlinehelpwriter.h"
-#include "gromacs/commandline/shellcompletions.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_int64_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_int64_t       var = str_to_int64_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);
-}
-
 int nenum(const char *const enumc[])
 {
     int i;
@@ -184,140 +81,6 @@ int nenum(const char *const enumc[])
     return i;
 }
 
-/* Read a number of arguments from the command line.
- * For etINT, etREAL and etCHAR an extra argument is read (when present)
- * for etBOOL the gmx_boolean option is changed to the negate value
- */
-static void get_pargs(int *argc, char *argv[], int nparg, t_pargs pa[])
-{
-    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 etINT64:
-                        *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;
-            }
-        }
-    }
-
-    /* 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;
@@ -335,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;
 
@@ -424,89 +187,141 @@ const char *opt2parg_enum(const char *option, int nparg, t_pargs pa[])
  * parse_common_args()
  */
 
-static void set_default_time_unit(const char *time_list[], gmx_bool bCanTime)
+namespace gmx
 {
-    int         i      = 0;
-    const char *select = NULL;
 
-    if (bCanTime)
-    {
-        select = getenv("GMXTIMEUNIT");
-        if (select != NULL)
-        {
-            i = 1;
-            while (time_list[i] && strcmp(time_list[i], select) != 0)
-            {
-                i++;
-            }
-        }
-    }
-    if (!bCanTime || select == NULL ||
-        time_list[i] == NULL || strcmp(time_list[i], select) != 0)
-    {
-        /* Set it to the default: ps */
-        i = 1;
-        while (time_list[i] && strcmp(time_list[i], "ps") != 0)
-        {
-            i++;
-        }
-
-    }
-    time_list[0] = time_list[i];
-}
-
-static void set_default_xvg_format(const char *xvg_list[])
+namespace
 {
-    int         i;
-    const char *select;
 
-    select = getenv("GMX_VIEW_XVG");
-    if (select == NULL)
-    {
-        /* The default is the first option */
-        xvg_list[0] = xvg_list[1];
-    }
-    else
+/*! \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)
     {
-        i = 1;
-        while (xvg_list[i] && strcmp(xvg_list[i], select) != 0)
+        ConstArrayRef<const char *>::const_iterator i =
+            std::find(xvgFormats.begin(), xvgFormats.end(), std::string(select));
+        if (i != xvgFormats.end())
         {
-            i++;
-        }
-        if (xvg_list[i] != NULL)
-        {
-            xvg_list[0] = xvg_list[i];
+            return i - xvgFormats.begin();
         }
         else
         {
-            xvg_list[0] = xvg_list[exvgNONE];
+            return exvgNONE - 1;
         }
     }
+    /* The default is the first option */
+    return 0;
 }
 
-static int add_parg(int npargs, t_pargs *pa, t_pargs *pa_add)
-{
-    memcpy(&(pa[npargs]), pa_add, sizeof(*pa_add));
-
-    return npargs+1;
-}
-
-namespace gmx
-{
-
-namespace
-{
-
 /*! \brief
- * Converts a t_filenm option into an Options option.
+ * Conversion helper between t_pargs/t_filenm and Options.
  *
- * \param     options Options object to add the new option to.
- * \param[in] fnm     t_filenm option to convert.
+ * 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
  */
-void filenmToOptions(Options *options, const t_filenm *fnm)
+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);
@@ -518,81 +333,131 @@ void filenmToOptions(Options *options, const t_filenm *fnm)
     {
         defName = ftp2defnm(fnm->ftp);
     }
-    // Since we are not (yet) using this for actual parsing, we don't need to
-    // set any storage.
-    options->addOption(
-            FileNameOption(name).defaultBasename(defName).legacyType(fnm->ftp)
-                .readWriteFlags(bRead, bWrite).required(!bOptional)
-                .libraryFile(bLibrary).multiValue(bMultiple)
-                .description(ftp2desc(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)));
 }
 
-/*! \brief
- * Converts a t_pargs option into an Options option.
- *
- * \param     options Options object to add the new option to.
- * \param[in] pa      t_pargs option to convert.
- *
- * \ingroup module_commandline
- */
-void pargsToOptions(Options *options, t_pargs *pa)
+void OptionsAdapter::pargsToOptions(Options *options, t_pargs *pa)
 {
-    const bool        bHidden = is_hidden(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);
-    // Since we are not (yet) using this for actual parsing, we can take some
-    // shortcuts and not set any storage where there is no direct
-    // correspondence in the types.
+    programArgs_.push_back(ProgramArgData(pa));
+    ProgramArgData   &data = programArgs_.back();
     switch (pa->type)
     {
         case etINT:
-            options->addOption(
-                IntegerOption(name).store(pa->u.i)
-                    .description(desc).hidden(bHidden));
+            data.optionInfo = options->addOption(
+                        IntegerOption(name).store(pa->u.i)
+                            .description(desc).hidden(bHidden));
             return;
         case etINT64:
-            options->addOption(
-                Int64Option(name).store(pa->u.is)
-                    .description(desc).hidden(bHidden));
+            data.optionInfo = options->addOption(
+                        Int64Option(name).store(pa->u.is)
+                            .description(desc).hidden(bHidden));
             return;
         case etREAL:
-            options->addOption(
-                RealOption(name).store(pa->u.r)
-                    .description(desc).hidden(bHidden));
+            data.optionInfo = options->addOption(
+                        RealOption(name).store(pa->u.r)
+                            .description(desc).hidden(bHidden));
             return;
         case etTIME:
-            options->addOption(
-                RealOption(name).store(pa->u.r).timeValue()
-                    .description(desc).hidden(bHidden));
+            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 : "");
-            options->addOption(
-                    StringOption(name).defaultValue(defValue)
-                        .description(desc).hidden(bHidden));
+            data.optionInfo = options->addOption(
+                        StringOption(name).store(&data.stringValue)
+                            .defaultValue(defValue)
+                            .description(desc).hidden(bHidden));
             return;
         }
         case etBOOL:
-            options->addOption(
-                BooleanOption(name).defaultValue(*pa->u.b)
-                    .description(desc).hidden(bHidden));
+            data.optionInfo = options->addOption(
+                        BooleanOption(name).store(&data.boolValue)
+                            .defaultValue(*pa->u.b)
+                            .description(desc).hidden(bHidden));
             return;
         case etRVEC:
-            options->addOption(
-                RealOption(name).store(*pa->u.rv).vector()
-                    .description(desc).hidden(bHidden));
+            data.optionInfo = options->addOption(
+                        RealOption(name).store(*pa->u.rv).vector()
+                            .description(desc).hidden(bHidden));
             return;
         case etENUM:
-            options->addOption(
-                StringOption(name).defaultEnumIndex(nenum(pa->u.c) - 1)
-                    .enumValueFromNullTerminatedArray(pa->u.c + 1)
-                    .description(desc).hidden(bHidden));
+        {
+            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
@@ -604,315 +469,132 @@ gmx_bool parse_common_args(int *argc, char *argv[], unsigned long Flags,
                            output_env_t *oenv)
 {
     /* This array should match the order of the enum in oenv.h */
-    const char *xvg_format[] = { NULL, "xmgrace", "xmgr", "none", NULL };
-    /* This array should match the order of the enum in oenv.h */
-    const char *time_units[] = {
-        NULL, "fs", "ps", "ns", "us", "ms", "s",
-        NULL
-    };
-    int         nicelevel = 0, debug_level = 0;
-    char       *deffnm    = NULL;
-    real        tbegin    = 0, tend = 0, tdelta = 0;
-    gmx_bool    bView     = FALSE;
-
-    t_pargs    *all_pa = NULL;
-
-#ifdef __sgi
-    int     npri      = 0;
-    t_pargs npri_pa   = {
-        "-npri", FALSE, etINT,   {&npri},
-        "HIDDEN Set non blocking priority (try 128)"
-    };
-#endif
-    t_pargs nice_pa   = {
-        "-nice", FALSE, etINT,   {&nicelevel},
-        "Set the nicelevel"
-    };
-    t_pargs deffnm_pa = {
-        "-deffnm", FALSE, etSTR, {&deffnm},
-        "Set the default filename for all file options"
-    };
-    t_pargs begin_pa  = {
-        "-b",    FALSE, etTIME,  {&tbegin},
-        "First frame (%t) to read from trajectory"
-    };
-    t_pargs end_pa    = {
-        "-e",    FALSE, etTIME,  {&tend},
-        "Last frame (%t) to read from trajectory"
-    };
-    t_pargs dt_pa     = {
-        "-dt",   FALSE, etTIME,  {&tdelta},
-        "Only use frame when t MOD dt = first time (%t)"
-    };
-    t_pargs view_pa   = {
-        "-w",    FALSE, etBOOL,  {&bView},
-        "View output [TT].xvg[tt], [TT].xpm[tt], [TT].eps[tt] and [TT].pdb[tt] files"
-    };
-    t_pargs xvg_pa    = {
-        "-xvg",  FALSE, etENUM,  {xvg_format},
-        "xvg plot formatting"
-    };
-    t_pargs time_pa   = {
-        "-tu",   FALSE, etENUM,  {time_units},
-        "Time unit"
-    };
-    /* Maximum number of extra arguments */
-#define EXTRA_PA 16
-
-    t_pargs  pca_pa[] = {
-        { "-debug", FALSE, etINT, {&debug_level},
-          "HIDDENWrite file with debug information, 1: short, 2: also x and f" },
-    };
-#define NPCA_PA asize(pca_pa)
-    gmx_bool bXvgr;
-    int      i, j, k, npall, max_pa;
+    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)
 
-    /* Check for double arguments */
-    for (i = 1; (i < *argc); i++)
+    try
     {
-        if (argv[i] && (strlen(argv[i]) > 1) && (!std::isdigit(argv[i][1])))
-        {
-            for (j = i+1; (j < *argc); j++)
-            {
-                if ( (argv[i][0] == '-') && (argv[j][0] == '-') &&
-                     (strcmp(argv[i], argv[j]) == 0) )
-                {
-                    if (FF(PCA_NOEXIT_ON_ARGS))
-                    {
-                        fprintf(stderr, "Double command line argument %s\n",
-                                argv[i]);
-                    }
-                    else
-                    {
-                        gmx_fatal(FARGS, "Double command line argument %s\n",
-                                  argv[i]);
-                    }
-                }
-            }
-        }
-    }
-    debug_gmx();
+        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;
 
-    /* Check ALL the flags ... */
-    max_pa = NPCA_PA + EXTRA_PA + npargs+1;
-    snew(all_pa, max_pa);
+        fileOptManager.disableInputOptionChecking(
+                FF(PCA_NOT_READ_NODE) || FF(PCA_DISABLE_INPUT_FILE_CHECKING));
+        options.addManager(&fileOptManager);
+        options.setDescription(gmx::constArrayRefFromArray<const char *>(desc, ndesc));
 
-    for (i = npall = 0; (i < static_cast<int>(NPCA_PA)); i++)
-    {
-        npall = add_parg(npall, all_pa, &(pca_pa[i]));
-    }
-
-#ifdef __sgi
-    const char *envstr = getenv("GMXNPRIALL");
-    if (envstr)
-    {
-        npri = strtol(envstr, NULL, 10);
-    }
-    if (FF(PCA_BE_NICE))
-    {
-        envstr = getenv("GMXNPRI");
-        if (envstr)
+        if (FF(PCA_CAN_SET_DEFFNM))
         {
-            npri = strtol(envstr, NULL, 10);
+            fileOptManager.addDefaultFileNameOption(&options, "deffnm");
         }
-    }
-    npall = add_parg(npall, all_pa, &npri_pa);
-#endif
-
-    if (FF(PCA_BE_NICE))
-    {
-        nicelevel = 19;
-    }
-    npall = add_parg(npall, all_pa, &nice_pa);
-
-    if (FF(PCA_CAN_SET_DEFFNM))
-    {
-        npall = add_parg(npall, all_pa, &deffnm_pa);
-    }
-    if (FF(PCA_CAN_BEGIN))
-    {
-        npall = add_parg(npall, all_pa, &begin_pa);
-    }
-    if (FF(PCA_CAN_END))
-    {
-        npall = add_parg(npall, all_pa, &end_pa);
-    }
-    if (FF(PCA_CAN_DT))
-    {
-        npall = add_parg(npall, all_pa, &dt_pa);
-    }
-    if (FF(PCA_TIME_UNIT))
-    {
-        npall = add_parg(npall, all_pa, &time_pa);
-    }
-    if (FF(PCA_CAN_VIEW))
-    {
-        npall = add_parg(npall, all_pa, &view_pa);
-    }
-
-    bXvgr = FALSE;
-    for (i = 0; (i < nfile); i++)
-    {
-        bXvgr = bXvgr ||  (fnm[i].ftp == efXVG);
-    }
-    if (bXvgr)
-    {
-        npall = add_parg(npall, all_pa, &xvg_pa);
-    }
-
-    /* Now append the program specific arguments */
-    for (i = 0; (i < npargs); i++)
-    {
-        npall = add_parg(npall, all_pa, &(pa[i]));
-    }
-
-    /* set etENUM options to default */
-    for (i = 0; (i < npall); i++)
-    {
-        if (all_pa[i].type == etENUM)
+        if (FF(PCA_CAN_BEGIN))
         {
-            all_pa[i].u.c[0] = all_pa[i].u.c[1];
+            options.addOption(
+                    gmx::DoubleOption("b").store(&tbegin).timeValue()
+                        .description("First frame (%t) to read from trajectory"));
         }
-    }
-    set_default_time_unit(time_units, FF(PCA_TIME_UNIT));
-    set_default_xvg_format(xvg_format);
-
-    /* Now parse all the command-line options */
-    get_pargs(argc, argv, npall, all_pa);
-
-    /* set program name, command line, and default values for output options */
-    output_env_init(oenv, *argc, argv, (time_unit_t)nenum(time_units), bView,
-                    (xvg_format_t)nenum(xvg_format), 0, debug_level);
-
-    /* Parse the file args */
-    parse_file_args(argc, argv, nfile, fnm, deffnm, !FF(PCA_NOT_READ_NODE));
-
-    /* Open the debug file */
-    if (debug_level > 0)
-    {
-        char buf[256];
-
-        if (gmx_mpi_initialized())
+        if (FF(PCA_CAN_END))
         {
-            sprintf(buf, "%s%d.debug", output_env_get_short_program_name(*oenv),
-                    gmx_node_rank());
+            options.addOption(
+                    gmx::DoubleOption("e").store(&tend).timeValue()
+                        .description("Last frame (%t) to read from trajectory"));
         }
-        else
+        if (FF(PCA_CAN_DT))
         {
-            sprintf(buf, "%s.debug", output_env_get_short_program_name(*oenv));
+            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"));
         }
 
-        init_debug(debug_level, buf);
-        fprintf(stderr, "Opening debug file %s (src code file %s, line %d)\n",
-                buf, __FILE__, __LINE__);
-    }
-
-    /* Now copy the results back... */
-    for (i = 0, k = npall-npargs; (i < npargs); i++, k++)
-    {
-        memcpy(&(pa[i]), &(all_pa[k]), (size_t)sizeof(pa[i]));
-    }
+        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"));
+        }
 
-#if (defined __sgi && USE_SGI_FPE)
-    doexceptions();
-#endif
+        /* 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]);
+        }
 
-    bool bExit = false;
-    try
-    {
         const gmx::CommandLineHelpContext *context =
             gmx::GlobalCommandLineHelpContext::get();
-        bExit = (context != NULL);
-        if (context != NULL && !(FF(PCA_QUIET)))
+        if (context != NULL)
         {
-            gmx::Options options(NULL, NULL);
-            options.setDescription(gmx::ConstArrayRef<const char *>(desc, ndesc));
-            for (i = 0; i < nfile; i++)
-            {
-                gmx::filenmToOptions(&options, &fnm[i]);
-            }
-            for (i = 0; i < npall; i++)
-            {
-                gmx::pargsToOptions(&options, &all_pa[i]);
-            }
+            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(output_env_get_time_unit(*oenv))
-                .setKnownIssues(gmx::ConstArrayRef<const char *>(bugs, nbugs))
+                .setTimeUnitString(timeUnitManager.timeUnitAsString())
+                .setKnownIssues(gmx::constArrayRefFromArray(bugs, nbugs))
                 .writeHelp(*context);
+            return FALSE;
         }
-    }
-    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 
-    /* Set the nice level */
-#ifdef __sgi
-    if (npri != 0 && !bExit)
-    {
-        schedctl(MPTS_RTPRI, 0, npri);
-    }
-#endif
+        /* Now parse all the command-line options */
+        gmx::CommandLineParser(&options).skipUnknown(FF(PCA_NOEXIT_ON_ARGS))
+            .parse(argc, argv);
+        options.finish();
 
-#ifdef HAVE_UNISTD_H
-#ifndef GMX_NO_NICE
-    /* The some system, e.g. the catamount kernel on cray xt3 do not have nice(2). */
-    if (nicelevel != 0 && !bExit)
-    {
-        static gmx_bool            nice_set   = FALSE; /* only set it once */
-        static tMPI_Thread_mutex_t init_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
-        tMPI_Thread_mutex_lock(&init_mutex);
-        if (!nice_set)
-        {
-            if (nice(nicelevel) == -1)
-            {
-                /* Do nothing, but use the return value to avoid warnings. */
-            }
-            nice_set = TRUE;
-        }
-        tMPI_Thread_mutex_unlock(&init_mutex);
-    }
-#endif
-#endif
+        /* 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);
 
-    /* convert time options, must be done after printing! */
+        timeUnitManager.scaleTimeOptions(&options);
 
-    for (i = 0; i < npall; i++)
-    {
-        if (all_pa[i].type == etTIME && all_pa[i].bSet)
+        /* Extract Time info from arguments */
+        // TODO: Use OptionInfo objects instead of string constants
+        if (FF(PCA_CAN_BEGIN) && options.isSet("b"))
         {
-            *all_pa[i].u.r *= output_env_get_time_invfactor(*oenv);
+            setTimeValue(TBEGIN, tbegin);
         }
-    }
-
-    /* Extract Time info from arguments */
-    if (FF(PCA_CAN_BEGIN) && opt2parg_bSet("-b", npall, all_pa))
-    {
-        setTimeValue(TBEGIN, opt2parg_real("-b", npall, all_pa));
-    }
-
-    if (FF(PCA_CAN_END) && opt2parg_bSet("-e", npall, all_pa))
-    {
-        setTimeValue(TEND, opt2parg_real("-e", npall, all_pa));
-    }
-
-    if (FF(PCA_CAN_DT) && opt2parg_bSet("-dt", npall, all_pa))
-    {
-        setTimeValue(TDELTA, opt2parg_real("-dt", npall, all_pa));
-    }
-
-    /* clear memory */
-    sfree(all_pa);
-
-    if (!FF(PCA_NOEXIT_ON_ARGS))
-    {
-        if (*argc > 1)
+        if (FF(PCA_CAN_END) && options.isSet("e"))
+        {
+            setTimeValue(TEND, tend);
+        }
+        if (FF(PCA_CAN_DT) && options.isSet("dt"))
         {
-            gmx_cmd(argv[1]);
+            setTimeValue(TDELTA, tdelta);
         }
+
+        adapter.copyValues(!FF(PCA_NOT_READ_NODE));
+
+        return TRUE;
     }
-    return !bExit;
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 #undef FF
 }