Move pargs.cpp into src/gromacs/commandline/
[alexxy/gromacs.git] / src / gromacs / commandline / pargs.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  *                        VERSION 3.2.0
10  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12  * Copyright (c) 2001-2004, The GROMACS development team,
13  * check out http://www.gromacs.org for more information.
14
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * If you want to redistribute modifications, please consider that
21  * scientific software is very special. Version control is crucial -
22  * bugs must be traceable. We will be happy to consider code for
23  * inclusion in the official distribution, but derived work must not
24  * be called official GROMACS. Details are found in the README & COPYING
25  * files - if they are missing, get the official version at www.gromacs.org.
26  *
27  * To help us fund GROMACS development, we humbly ask that you cite
28  * the papers on the package - you can find them in the top README file.
29  *
30  * For more info, check our website at http://www.gromacs.org
31  *
32  * And Hey:
33  * GROningen Mixture of Alchemy and Childrens' Stories
34  */
35 /* This file is completely threadsafe - keep it that way! */
36 #include "gromacs/commandline/pargs.h"
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 #include <cctype>
43 #include <cstdio>
44 #include <cstdlib>
45 #include <cstring>
46
47 #include "gromacs/legacyheaders/gmx_fatal.h"
48 #include "gromacs/legacyheaders/smalloc.h"
49 #include "gromacs/legacyheaders/string2.h"
50
51 #include "gromacs/utility/gmxassert.h"
52
53 /* The source code in this file should be thread-safe.
54       Please keep it that way. */
55
56 static void usage(const char *type, const char *arg)
57 {
58     GMX_ASSERT(arg != NULL, "NULL command-line argument should not occur");
59     gmx_fatal(FARGS, "Expected %s argument for option %s\n", type, arg);
60 }
61
62 /* Scan an int for argument argv[*i] from argument at argv[*i + 1].
63  * eg: -p 32.  argv[*i] is only used for error reporting.
64  * If there is no value, or the conversion is not successful, the
65  * routine exits with an error, otherwise it returns the value found.
66  * *i is incremented once.
67  */
68 static int iscan(int argc, char *argv[], int *i)
69 {
70     const char *const arg = argv[*i];
71     if (argc <= (*i)+1)
72     {
73         usage("an integer", arg);
74     }
75     const char *const value = argv[++(*i)];
76     char             *endptr;
77     int               var = std::strtol(value, &endptr, 10);
78     if (*value == '\0' || *endptr != '\0')
79     {
80         usage("an integer", arg);
81     }
82     return var;
83 }
84
85 /* Same as above, but for large integer values */
86 static gmx_large_int_t istepscan(int argc, char *argv[], int *i)
87 {
88     const char *const arg = argv[*i];
89     if (argc <= (*i)+1)
90     {
91         usage("an integer", arg);
92     }
93     const char *const value = argv[++(*i)];
94     char             *endptr;
95     gmx_large_int_t   var = str_to_large_int_t(value, &endptr);
96     if (*value == '\0' || *endptr != '\0')
97     {
98         usage("an integer", arg);
99     }
100     return var;
101 }
102
103 /* Routine similar to the above, but working on doubles. */
104 static double dscan(int argc, char *argv[], int *i)
105 {
106     const char *const arg = argv[*i];
107     if (argc <= (*i)+1)
108     {
109         usage("a real", arg);
110     }
111     const char *const value = argv[++(*i)];
112     char             *endptr;
113     double            var = std::strtod(value, &endptr);
114     if (*value == '\0' || *endptr != '\0')
115     {
116         usage("a real", arg);
117     }
118     return var;
119 }
120
121 /* Routine similar to the above, but working on strings. The pointer
122  * returned is a pointer to the argv field.
123  */
124 static char *sscan(int argc, char *argv[], int *i)
125 {
126     if (argc > (*i)+1)
127     {
128         if ( (argv[(*i)+1][0] == '-') && (argc > (*i)+2) &&
129              (argv[(*i)+2][0] != '-') )
130         {
131             fprintf(stderr, "Possible missing string argument for option %s\n\n",
132                     argv[*i]);
133         }
134     }
135     else
136     {
137         usage("a string", argv[*i]);
138     }
139
140     return argv[++(*i)];
141 }
142
143 gmx_bool is_hidden(t_pargs *pa)
144 {
145     return ((strstr(pa->desc, "HIDDEN") != NULL) ||
146             (strstr(pa->desc, "[hidden]") != NULL));
147 }
148
149 int nenum(const char *const enumc[])
150 {
151     int i;
152
153     i = 1;
154     /* we *can* compare pointers directly here! */
155     while (enumc[i] && enumc[0] != enumc[i])
156     {
157         i++;
158     }
159
160     return i;
161 }
162
163 void get_pargs(int *argc, char *argv[], int nparg, t_pargs pa[], gmx_bool bKeepArgs)
164 {
165     int       i, j, k, match;
166     gmx_bool *bKeep;
167     char      buf[32];
168     char     *ptr;
169
170     snew(bKeep, *argc+1);
171     bKeep[0]     = TRUE;
172     bKeep[*argc] = TRUE;
173
174     for (i = 1; (i < *argc); i++)
175     {
176         bKeep[i] = TRUE;
177         for (j = 0; (j < nparg); j++)
178         {
179             if (pa[j].type == etBOOL)
180             {
181                 sprintf(buf, "-no%s", pa[j].option+1);
182                 if (strcmp(pa[j].option, argv[i]) == 0)
183                 {
184                     *pa[j].u.b = TRUE;
185                     pa[j].bSet = TRUE;
186                     bKeep[i]   = FALSE;
187                 }
188                 else if (strcmp(buf, argv[i]) == 0)
189                 {
190                     *pa[j].u.b = FALSE;
191                     pa[j].bSet = TRUE;
192                     bKeep[i]   = FALSE;
193                 }
194             }
195             else if (strcmp(pa[j].option, argv[i]) == 0)
196             {
197                 if (pa[j].bSet)
198                 {
199                     fprintf(stderr, "Setting option %s more than once!\n",
200                             pa[j].option);
201                 }
202                 pa[j].bSet = TRUE;
203                 bKeep[i]   = FALSE;
204                 switch (pa[j].type)
205                 {
206                     case etINT:
207                         *pa[j].u.i = iscan(*argc, argv, &i);
208                         break;
209                     case etGMX_LARGE_INT:
210                         *pa[j].u.is = istepscan(*argc, argv, &i);
211                         break;
212                     case etTIME:
213                     case etREAL:
214                         *pa[j].u.r = dscan(*argc, argv, &i);
215                         break;
216                     case etSTR:
217                         *(pa[j].u.c) = sscan(*argc, argv, &i);
218                         break;
219                     case etENUM:
220                         match = -1;
221                         ptr   = sscan(*argc, argv, &i);
222                         for (k = 1; (pa[j].u.c[k] != NULL); k++)
223                         {
224                             /* only check ptr against beginning of
225                                pa[j].u.c[k] */
226                             if (gmx_strncasecmp(ptr, pa[j].u.c[k], strlen(ptr)) == 0)
227                             {
228                                 if ( ( match == -1 ) ||
229                                      ( strlen(pa[j].u.c[k]) <
230                                        strlen(pa[j].u.c[match]) ) )
231                                 {
232                                     match = k;
233                                 }
234                             }
235                         }
236                         if (match != -1)
237                         {
238                             pa[j].u.c[0] = pa[j].u.c[match];
239                         }
240                         else
241                         {
242                             gmx_fatal(FARGS, "Invalid argument %s for option %s",
243                                       ptr, pa[j].option);
244                         }
245                         break;
246                     case etRVEC:
247                         (*pa[j].u.rv)[0] = dscan(*argc, argv, &i);
248                         if ( (i+1 == *argc) ||
249                              ( (argv[i+1][0] == '-') &&
250                                !isdigit(argv[i+1][1]) ) )
251                         {
252                             (*pa[j].u.rv)[1]     =
253                                 (*pa[j].u.rv)[2] =
254                                     (*pa[j].u.rv)[0];
255                         }
256                         else
257                         {
258                             bKeep[i]         = FALSE;
259                             (*pa[j].u.rv)[1] = dscan(*argc, argv, &i);
260                             if ( (i+1 == *argc) ||
261                                  ( (argv[i+1][0] == '-') &&
262                                    !isdigit(argv[i+1][1]) ) )
263                             {
264                                 gmx_fatal(FARGS,
265                                           "%s: vector must have 1 or 3 real parameters",
266                                           pa[j].option);
267                             }
268                             bKeep[i]         = FALSE;
269                             (*pa[j].u.rv)[2] = dscan(*argc, argv, &i);
270                         }
271                         break;
272                     default:
273                         gmx_fatal(FARGS, "Invalid type %d in pargs", pa[j].type);
274                 }
275                 /* i may be incremented, so set it to not keep */
276                 bKeep[i] = FALSE;
277             }
278         }
279     }
280     if (!bKeepArgs)
281     {
282         /* Remove used entries */
283         for (i = j = 0; (i <= *argc); i++)
284         {
285             if (bKeep[i])
286             {
287                 argv[j++] = argv[i];
288             }
289         }
290         (*argc) = j-1;
291     }
292     sfree(bKeep);
293 }
294
295 int opt2parg_int(const char *option, int nparg, t_pargs pa[])
296 {
297     int i;
298
299     for (i = 0; (i < nparg); i++)
300     {
301         if (strcmp(pa[i].option, option) == 0)
302         {
303             return *pa[i].u.i;
304         }
305     }
306
307     gmx_fatal(FARGS, "No integer option %s in pargs", option);
308
309     return 0;
310 }
311
312 gmx_bool opt2parg_gmx_bool(const char *option, int nparg, t_pargs pa[])
313 {
314     int i;
315
316     for (i = 0; (i < nparg); i++)
317     {
318         if (strcmp(pa[i].option, option) == 0)
319         {
320             return *pa[i].u.b;
321         }
322     }
323
324     gmx_fatal(FARGS, "No boolean option %s in pargs", option);
325
326     return FALSE;
327 }
328
329 real opt2parg_real(const char *option, int nparg, t_pargs pa[])
330 {
331     int i;
332
333     for (i = 0; (i < nparg); i++)
334     {
335         if (strcmp(pa[i].option, option) == 0)
336         {
337             return *pa[i].u.r;
338         }
339     }
340
341     gmx_fatal(FARGS, "No real option %s in pargs", option);
342
343     return 0.0;
344 }
345
346 const char *opt2parg_str(const char *option, int nparg, t_pargs pa[])
347 {
348     int i;
349
350     for (i = 0; (i < nparg); i++)
351     {
352         if (strcmp(pa[i].option, option) == 0)
353         {
354             return *(pa[i].u.c);
355         }
356     }
357
358     gmx_fatal(FARGS, "No string option %s in pargs", option);
359
360     return NULL;
361 }
362
363 gmx_bool opt2parg_bSet(const char *option, int nparg, t_pargs pa[])
364 {
365     int i;
366
367     for (i = 0; (i < nparg); i++)
368     {
369         if (strcmp(pa[i].option, option) == 0)
370         {
371             return pa[i].bSet;
372         }
373     }
374
375     gmx_fatal(FARGS, "No such option %s in pargs", option);
376
377     return FALSE; /* Too make some compilers happy */
378 }
379
380 const char *opt2parg_enum(const char *option, int nparg, t_pargs pa[])
381 {
382     int i;
383
384     for (i = 0; (i < nparg); i++)
385     {
386         if (strcmp(pa[i].option, option) == 0)
387         {
388             return pa[i].u.c[0];
389         }
390     }
391
392     gmx_fatal(FARGS, "No such option %s in pargs", option);
393
394     return NULL;
395 }