Removed support for multiple entries in mdp files
[gromacs.git] / src / gromacs / fileio / readinp.cpp
blobcc996414856b096b2f0fc540f96d6a272ead5c99
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
37 #include "gmxpre.h"
39 #include "readinp.h"
41 #include <cstdio>
42 #include <cstdlib>
43 #include <cstring>
45 #include <algorithm>
47 #include "gromacs/fileio/gmxfio.h"
48 #include "gromacs/fileio/warninp.h"
49 #include "gromacs/utility/binaryinformation.h"
50 #include "gromacs/utility/cstringutil.h"
51 #include "gromacs/utility/exceptions.h"
52 #include "gromacs/utility/fatalerror.h"
53 #include "gromacs/utility/futil.h"
54 #include "gromacs/utility/keyvaluetreebuilder.h"
55 #include "gromacs/utility/programcontext.h"
56 #include "gromacs/utility/qsort_threadsafe.h"
57 #include "gromacs/utility/smalloc.h"
59 t_inpfile *read_inpfile(const char *fn, int *ninp,
60 warninp_t wi)
62 FILE *in;
63 char buf[STRLEN], lbuf[STRLEN], rbuf[STRLEN], warn_buf[STRLEN];
64 char *ptr, *cptr;
65 t_inpfile *inp = NULL;
66 int nin, lc, i, j, k;
69 if (debug)
71 fprintf(debug, "Reading MDP file %s\n", fn);
74 in = gmx_ffopen(fn, "r");
76 nin = lc = 0;
79 ptr = fgets2(buf, STRLEN-1, in);
80 lc++;
81 set_warning_line(wi, fn, lc);
82 if (ptr)
84 // TODO This parsing should be using strip_comment, trim,
85 // strchr, etc. rather than re-inventing wheels.
87 /* Strip comment */
88 if ((cptr = std::strchr(buf, COMMENTSIGN)) != NULL)
90 *cptr = '\0';
92 /* Strip spaces */
93 trim(buf);
95 for (j = 0; (buf[j] != '=') && (buf[j] != '\0'); j++)
99 if (buf[j] == '\0')
101 if (j > 0)
103 if (debug)
105 fprintf(debug, "No = on line %d in file %s, ignored\n", lc, fn);
109 else
111 for (i = 0; (i < j); i++)
113 lbuf[i] = buf[i];
115 lbuf[i] = '\0';
116 trim(lbuf);
117 if (lbuf[0] == '\0')
119 if (debug)
121 fprintf(debug, "Empty left hand side on line %d in file %s, ignored\n", lc, fn);
124 else
126 for (i = j+1, k = 0; (buf[i] != '\0'); i++, k++)
128 rbuf[k] = buf[i];
130 rbuf[k] = '\0';
131 trim(rbuf);
132 if (rbuf[0] == '\0')
134 if (debug)
136 fprintf(debug, "Empty right hand side on line %d in file %s, ignored\n", lc, fn);
139 else
141 /* Now finally something sensible; check for duplicates */
142 int found_index = search_einp(nin, inp, lbuf);
144 if (found_index == -1)
146 /* add a new item */
147 srenew(inp, ++nin);
148 inp[nin-1].inp_count = 1;
149 inp[nin-1].count = 0;
150 inp[nin-1].bObsolete = FALSE;
151 inp[nin-1].bSet = FALSE;
152 inp[nin-1].name = gmx_strdup(lbuf);
153 inp[nin-1].value = gmx_strdup(rbuf);
155 else
157 sprintf(warn_buf,
158 "Parameter \"%s\" doubly defined\n",
159 lbuf);
160 warning_error(wi, warn_buf);
167 while (ptr);
169 gmx_ffclose(in);
171 if (debug)
173 fprintf(debug, "Done reading MDP file, there were %d entries in there\n",
174 nin);
177 *ninp = nin;
179 return inp;
182 gmx::KeyValueTreeObject flatKeyValueTreeFromInpFile(int ninp, t_inpfile inp[])
184 gmx::KeyValueTreeBuilder builder;
185 auto root = builder.rootObject();
186 for (int i = 0; i < ninp; ++i)
188 const char *value = inp[i].value;
189 root.addValue<std::string>(inp[i].name, value != nullptr ? value : "");
191 return builder.build();
195 static int inp_comp(const void *a, const void *b)
197 return (reinterpret_cast<const t_inpfile *>(a))->count - (reinterpret_cast<const t_inpfile *>(b))->count;
200 static void sort_inp(int ninp, t_inpfile inp[])
202 int i, mm;
204 mm = -1;
205 for (i = 0; (i < ninp); i++)
207 mm = std::max(mm, inp[i].count);
209 for (i = 0; (i < ninp); i++)
211 if (inp[i].count == 0)
213 inp[i].count = mm++;
216 gmx_qsort(inp, ninp, static_cast<size_t>(sizeof(inp[0])), inp_comp);
219 void write_inpfile(const char *fn, int ninp, t_inpfile inp[], gmx_bool bHaltOnUnknown,
220 warninp_t wi)
222 FILE *out;
223 int i;
224 char warn_buf[STRLEN];
226 sort_inp(ninp, inp);
227 out = gmx_fio_fopen(fn, "w");
228 nice_header(out, fn);
231 gmx::BinaryInformationSettings settings;
232 settings.generatedByHeader(true);
233 settings.linePrefix(";\t");
234 gmx::printBinaryInformation(out, gmx::getProgramContext(), settings);
236 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
238 for (i = 0; (i < ninp); i++)
240 if (inp[i].bSet)
242 if (inp[i].name[0] == ';' || (strlen(inp[i].name) > 2 && inp[i].name[1] == ';'))
244 fprintf(out, "%-24s\n", inp[i].name);
246 else
248 fprintf(out, "%-24s = %s\n", inp[i].name, inp[i].value ? inp[i].value : "");
251 else if (!inp[i].bObsolete)
253 sprintf(warn_buf, "Unknown left-hand '%s' in parameter file\n",
254 inp[i].name);
255 if (bHaltOnUnknown)
257 warning_error(wi, warn_buf);
259 else
261 warning(wi, warn_buf);
265 gmx_fio_fclose(out);
267 check_warning_error(wi, FARGS);
270 void replace_inp_entry(int ninp, t_inpfile *inp, const char *old_entry, const char *new_entry)
272 int i;
274 for (i = 0; (i < ninp); i++)
276 if (gmx_strcasecmp_min(old_entry, inp[i].name) == 0)
278 if (new_entry)
280 fprintf(stderr, "Replacing old mdp entry '%s' by '%s'\n",
281 inp[i].name, new_entry);
282 sfree(inp[i].name);
283 inp[i].name = gmx_strdup(new_entry);
285 else
287 fprintf(stderr, "Ignoring obsolete mdp entry '%s'\n",
288 inp[i].name);
289 inp[i].bObsolete = TRUE;
295 int search_einp(int ninp, const t_inpfile *inp, const char *name)
297 int i;
299 if (inp == NULL)
301 return -1;
303 for (i = 0; i < ninp; i++)
305 if (gmx_strcasecmp_min(name, inp[i].name) == 0)
307 return i;
310 return -1;
313 void mark_einp_set(int ninp, t_inpfile *inp, const char *name)
315 int i = search_einp(ninp, inp, name);
316 if (i != -1)
318 inp[i].count = inp[0].inp_count++;
319 inp[i].bSet = TRUE;
323 static int get_einp(int *ninp, t_inpfile **inp, const char *name)
325 int i;
326 int notfound = FALSE;
328 i = search_einp(*ninp, *inp, name);
329 if (i == -1)
331 notfound = TRUE;
332 i = (*ninp)++;
333 srenew(*inp, (*ninp));
334 (*inp)[i].name = gmx_strdup(name);
335 (*inp)[i].bSet = TRUE;
337 (*inp)[i].count = (*inp)[0].inp_count++;
338 (*inp)[i].bSet = TRUE;
339 if (debug)
341 fprintf(debug, "Inp %d = %s\n", (*inp)[i].count, (*inp)[i].name);
344 /*if (i == (*ninp)-1)*/
345 if (notfound)
347 return -1;
349 else
351 return i;
355 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
356 int get_eint(int *ninp, t_inpfile **inp, const char *name, int def,
357 warninp_t wi)
359 char buf[32], *ptr, warn_buf[STRLEN];
360 int ii;
361 int ret;
363 ii = get_einp(ninp, inp, name);
365 if (ii == -1)
367 sprintf(buf, "%d", def);
368 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
370 return def;
372 else
374 ret = std::strtol((*inp)[ii].value, &ptr, 10);
375 if (*ptr != '\0')
377 sprintf(warn_buf, "Right hand side '%s' for parameter '%s' in parameter file is not an integer value\n", (*inp)[ii].value, (*inp)[ii].name);
378 warning_error(wi, warn_buf);
381 return ret;
385 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
386 gmx_int64_t get_eint64(int *ninp, t_inpfile **inp,
387 const char *name, gmx_int64_t def,
388 warninp_t wi)
390 char buf[32], *ptr, warn_buf[STRLEN];
391 int ii;
392 gmx_int64_t ret;
394 ii = get_einp(ninp, inp, name);
396 if (ii == -1)
398 sprintf(buf, "%" GMX_PRId64, def);
399 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
401 return def;
403 else
405 ret = str_to_int64_t((*inp)[ii].value, &ptr);
406 if (*ptr != '\0')
408 sprintf(warn_buf, "Right hand side '%s' for parameter '%s' in parameter file is not an integer value\n", (*inp)[ii].value, (*inp)[ii].name);
409 warning_error(wi, warn_buf);
412 return ret;
416 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
417 double get_ereal(int *ninp, t_inpfile **inp, const char *name, double def,
418 warninp_t wi)
420 char buf[32], *ptr, warn_buf[STRLEN];
421 int ii;
422 double ret;
424 ii = get_einp(ninp, inp, name);
426 if (ii == -1)
428 sprintf(buf, "%g", def);
429 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
431 return def;
433 else
435 ret = strtod((*inp)[ii].value, &ptr);
436 if (*ptr != '\0')
438 sprintf(warn_buf, "Right hand side '%s' for parameter '%s' in parameter file is not a real value\n", (*inp)[ii].value, (*inp)[ii].name);
439 warning_error(wi, warn_buf);
442 return ret;
446 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
447 const char *get_estr(int *ninp, t_inpfile **inp, const char *name, const char *def)
449 char buf[32];
450 int ii;
452 ii = get_einp(ninp, inp, name);
454 if (ii == -1)
456 if (def)
458 sprintf(buf, "%s", def);
459 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
461 else
463 (*inp)[(*ninp)-1].value = NULL;
466 return def;
468 else
470 return (*inp)[ii].value;
474 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
475 int get_eeenum(int *ninp, t_inpfile **inp, const char *name, const char **defs,
476 warninp_t wi)
478 int ii, i, j;
479 int n = 0;
480 char buf[STRLEN];
482 ii = get_einp(ninp, inp, name);
484 if (ii == -1)
486 (*inp)[(*ninp)-1].value = gmx_strdup(defs[0]);
488 return 0;
491 for (i = 0; (defs[i] != NULL); i++)
493 if (gmx_strcasecmp_min(defs[i], (*inp)[ii].value) == 0)
495 break;
499 if (defs[i] == NULL)
501 n += sprintf(buf, "Invalid enum '%s' for variable %s, using '%s'\n",
502 (*inp)[ii].value, name, defs[0]);
503 n += sprintf(buf+n, "Next time use one of:");
504 j = 0;
505 while (defs[j])
507 n += sprintf(buf+n, " '%s'", defs[j]);
508 j++;
510 if (wi != NULL)
512 warning_error(wi, buf);
514 else
516 fprintf(stderr, "%s\n", buf);
519 (*inp)[ii].value = gmx_strdup(defs[0]);
521 return 0;
524 return i;
527 int get_eenum(int *ninp, t_inpfile **inp, const char *name, const char **defs)
529 return get_eeenum(ninp, inp, name, defs, NULL);