Add grompp check for usage of "define" field in mdp
[gromacs.git] / src / gromacs / gmxpreprocess / gmxcpp.cpp
bloba8e621ca342a16c925ce0a0cf2bc4f39e872b68f
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,2017,2018,2019, 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 "gmxcpp.h"
41 #include <cctype>
42 #include <cerrno>
43 #include <climits>
44 #include <cmath>
45 #include <cstdio>
46 #include <cstdlib>
47 #include <cstring>
49 #include <algorithm>
50 #include <memory>
52 #include <unordered_set>
53 #include <sys/types.h>
55 #include "gromacs/utility/arrayref.h"
56 #include "gromacs/utility/cstringutil.h"
57 #include "gromacs/utility/dir_separator.h"
58 #include "gromacs/utility/fatalerror.h"
59 #include "gromacs/utility/futil.h"
60 #include "gromacs/utility/gmxassert.h"
62 struct t_define
64 std::string name;
65 std::string def;
68 /* enum used for handling ifdefs */
69 enum {
70 eifTRUE, eifFALSE, eifIGNORE, eifNR
73 struct gmx_cpp
75 std::shared_ptr < std::vector < t_define>> defines;
76 std::shared_ptr < std::vector < std::string>> includes;
77 std::unordered_set<std::string> unmatched_defines;
78 FILE *fp = nullptr;
79 std::string path;
80 std::string cwd;
81 std::string fn;
82 std::string line;
83 int line_nr;
84 std::vector<int> ifdefs;
85 struct gmx_cpp *child = nullptr;
86 struct gmx_cpp *parent = nullptr;
89 static bool is_word_end(char c)
91 return !((isalnum(c) != 0) || c == '_');
94 static const char *strstrw(const char *buf, const char *word)
96 const char *ptr;
98 while ((ptr = strstr(buf, word)) != nullptr)
100 /* Check if we did not find part of a longer word */
101 if (ptr &&
102 is_word_end(ptr[strlen(word)]) &&
103 (((ptr > buf) && is_word_end(ptr[-1])) || (ptr == buf)))
105 return ptr;
108 buf = ptr + strlen(word);
110 return nullptr;
113 /* Finds a preprocessor directive, whose name (after the '#') is
114 * returned in *name, and the remainder of the line after leading
115 * whitespace, without trailing whitespace, is returned in *val
117 static bool find_directive(const char *buf,
118 std::string *name,
119 std::string *val)
121 /* Skip initial whitespace */
122 while (isspace(*buf))
124 ++buf;
126 /* Check if this is a directive */
127 if (*buf != '#')
129 return FALSE;
131 /* Skip the hash and any space after it */
132 ++buf;
133 while (isspace(*buf))
135 ++buf;
137 /* Set the name pointer and find the next space */
138 name->clear();
139 while (*buf != '\0' && !isspace(*buf))
141 *name += *buf;
142 ++buf;
144 /* Set the end of the name here, and skip any space */
145 if (*buf != '\0')
147 ++buf;
148 while (isspace(*buf))
150 ++buf;
153 /* Check if anything is remaining */
154 if (*buf != '\0')
156 *val = buf;
157 // Remove trailing whitespace
158 while (!val->empty() && isspace(val->back()))
160 val->resize(val->size() - 1);
163 else
165 val->clear();
168 return TRUE;
171 static bool is_ifdeffed_out(gmx::ArrayRef<const int> ifdefs)
173 return (!ifdefs.empty() && ifdefs.back() != eifTRUE);
176 static void add_include(std::vector<std::string> *includes,
177 const char *includePath)
179 GMX_RELEASE_ASSERT(includes, "Need valid includes");
180 GMX_RELEASE_ASSERT(includePath, "Need a valid include path");
182 for (const std::string &include : *includes)
184 if (strcmp(include.c_str(), includePath) == 0)
186 return;
190 includes->push_back(includePath);
193 static void add_define(std::vector<t_define> *defines,
194 const std::string &name,
195 const char *value)
197 GMX_RELEASE_ASSERT(defines, "Need defines");
198 GMX_RELEASE_ASSERT(value, "Need a value");
200 for (t_define &define : *defines)
202 if (define.name == name)
204 define.def = value;
205 return;
209 defines->push_back({ name, value });
212 /* Open the file to be processed. The handle variable holds internal
213 info for the cpp emulator. Return integer status */
214 static int
215 cpp_open_file(const char *filenm,
216 gmx_cpp_t *handle,
217 char **cppopts,
218 std::shared_ptr < std::vector < t_define>> *definesFromParent,
219 std::shared_ptr < std::vector < std::string>> *includesFromParent)
221 // TODO: We should avoid new/delete, we should use Pimpl instead
222 gmx_cpp *cpp = new gmx_cpp;
223 *handle = cpp;
225 if (definesFromParent)
227 cpp->defines = *definesFromParent;
229 else
231 cpp->defines = std::make_shared < std::vector < t_define>>();
234 if (includesFromParent)
236 cpp->includes = *includesFromParent;
238 else
240 cpp->includes = std::make_shared < std::vector < std::string>>();
243 /* First process options, they might be necessary for opening files
244 (especially include statements). */
245 int i = 0;
246 if (cppopts)
248 while (cppopts[i])
250 if (strstr(cppopts[i], "-I") == cppopts[i])
252 add_include(cpp->includes.get(), cppopts[i] + 2);
254 if (strstr(cppopts[i], "-D") == cppopts[i])
256 /* If the option contains a =, split it into name and value. */
257 char *ptr = strchr(cppopts[i], '=');
258 if (ptr)
260 std::string buf = cppopts[i] + 2;
261 buf.resize(ptr - cppopts[i] - 2);
262 add_define(cpp->defines.get(), buf, ptr + 1);
263 cpp->unmatched_defines.insert(buf);
266 else
268 add_define(cpp->defines.get(), cppopts[i] + 2, "");
269 cpp->unmatched_defines.insert(cppopts[i] + 2);
272 i++;
276 /* Find the file. First check whether it is in the current directory. */
277 if (gmx_fexist(filenm))
279 cpp->fn = filenm;
281 else
283 /* If not, check all the paths given with -I. */
284 for (const std::string &include : *cpp->includes)
286 std::string buf = include + "/" + filenm;
287 if (gmx_fexist(buf))
289 cpp->fn = buf;
290 break;
293 /* If still not found, check the Gromacs library search path. */
294 if (cpp->fn.empty())
296 cpp->fn = gmx::findLibraryFile(filenm, false, false);
299 if (cpp->fn.empty())
301 gmx_fatal(FARGS, "Topology include file \"%s\" not found", filenm);
303 /* If the file name has a path component, we need to change to that
304 * directory. Note that we - just as C - always use UNIX path separators
305 * internally in include file names.
307 size_t pos = cpp->fn.rfind('/');
308 size_t pos2 = cpp->fn.rfind(DIR_SEPARATOR);
310 if (pos == std::string::npos || (pos2 != std::string::npos && pos2 > pos))
312 pos = pos2;
314 if (pos != std::string::npos)
316 cpp->path = cpp->fn;
317 cpp->path.resize(pos);
318 cpp->fn.erase(0, pos + 1);
320 char buf[STRLEN];
321 gmx_getcwd(buf, STRLEN);
322 cpp->cwd = buf;
324 gmx_chdir(cpp->path.c_str());
326 cpp->line.clear();
327 cpp->line_nr = 0;
328 cpp->ifdefs.clear();
329 cpp->child = nullptr;
330 cpp->parent = nullptr;
331 if (cpp->fp == nullptr)
333 cpp->fp = fopen(cpp->fn.c_str(), "r");
335 if (cpp->fp == nullptr)
337 switch (errno)
339 case EINVAL:
340 default:
341 return eCPP_UNKNOWN;
344 return eCPP_OK;
347 /* Open the file to be processed. The handle variable holds internal
348 info for the cpp emulator. Return integer status */
349 int cpp_open_file(const char *filenm, gmx_cpp_t *handle, char **cppopts)
351 return cpp_open_file(filenm, handle, cppopts, nullptr, nullptr);
354 /* Note that dval might be null, e.g. when handling a line like '#define */
355 static int
356 process_directive(gmx_cpp_t *handlep,
357 const std::string &dname,
358 const std::string &dval)
360 gmx_cpp_t handle = *handlep;
362 std::vector<int> &ifdefs = handle->ifdefs;
364 /* #ifdef or ifndef statement */
365 bool bIfdef = (dname == "ifdef");
366 bool bIfndef = (dname == "ifndef");
367 if (bIfdef || bIfndef)
369 if (is_ifdeffed_out(ifdefs))
371 handle->ifdefs.push_back(eifIGNORE);
373 else
375 // A bare '#ifdef' or '#ifndef' is invalid
376 if (dval.empty())
378 return eCPP_SYNTAX;
380 bool found = false;
381 for (const t_define &define : *handle->defines)
383 if (define.name == dval)
385 // erase from unmatched_defines in original handle
386 gmx_cpp_t root = handle;
387 while (root->parent != nullptr)
389 root = root->parent;
391 root->unmatched_defines.erase(dval);
393 found = true;
394 break;
397 if ((bIfdef && found) || (bIfndef && !found))
399 ifdefs.push_back(eifTRUE);
401 else
403 ifdefs.push_back(eifFALSE);
406 return eCPP_OK;
409 /* #else statement */
410 if (dname == "else")
412 if (ifdefs.empty())
414 return eCPP_SYNTAX;
416 if (ifdefs.back() == eifTRUE)
418 ifdefs.back() = eifFALSE;
420 else if (ifdefs.back() == eifFALSE)
422 ifdefs.back() = eifTRUE;
424 return eCPP_OK;
427 /* #endif statement */
428 if (dname == "endif")
430 if (ifdefs.empty())
432 return eCPP_SYNTAX;
434 ifdefs.erase(ifdefs.end() - 1);
435 return eCPP_OK;
438 /* Check whether we're not ifdeffed out. The order of this statement
439 is important. It has to come after #ifdef, #else and #endif, but
440 anything else should be ignored. */
441 if (is_ifdeffed_out(ifdefs))
443 return eCPP_OK;
446 /* Check for include statements */
447 if (dname == "include")
449 int len = -1;
450 int i0 = 0;
451 // A bare '#include' is an invalid line
452 if (dval.empty())
454 return eCPP_SYNTAX;
456 for (size_t i1 = 0; i1 < dval.size(); i1++)
458 if ((dval[i1] == '"') || (dval[i1] == '<') || (dval[i1] == '>'))
460 if (len == -1)
462 i0 = i1+1;
463 len = 0;
465 else
467 break;
470 else if (len >= 0)
472 len++;
475 if (len == -1)
477 return eCPP_SYNTAX;
479 std::string inc_fn = dval.substr(i0, len);
481 /* Open include file and store it as a child in the handle structure */
482 int status = cpp_open_file(inc_fn.c_str(), &(handle->child), nullptr,
483 &handle->defines, &handle->includes);
484 if (status != eCPP_OK)
486 handle->child = nullptr;
487 return status;
489 /* Make a linked list of open files and move on to the include file */
490 handle->child->parent = handle;
491 *handlep = handle->child;
492 return eCPP_OK;
495 /* #define statement */
496 if (dname == "define")
498 // A bare '#define' is an invalid line
499 if (dval.empty())
501 return eCPP_SYNTAX;
503 /* Split it into name and value. */
504 const char *ptr = dval.c_str();
505 while ((*ptr != '\0') && !isspace(*ptr))
507 ptr++;
509 std::string name = dval.substr(0, ptr - dval.c_str());
511 while ((*ptr != '\0') && isspace(*ptr))
513 ptr++;
516 add_define(handle->defines.get(), name, ptr);
517 return eCPP_OK;
520 /* #undef statement */
521 if (dname == "undef")
523 // A bare '#undef' is an invalid line
524 if (dval.empty())
526 return eCPP_SYNTAX;
528 std::vector<t_define> &defines = *handle->defines;
529 for (size_t i = 0; i < defines.size(); i++)
531 if (defines[i].name == dval)
533 defines.erase(defines.begin() + i);
534 break;
538 return eCPP_OK;
541 /* If we haven't matched anything, this is an unknown directive */
542 return eCPP_SYNTAX;
545 /* Return one whole line from the file into buf which holds at most n
546 characters, for subsequent processing. Returns integer status. This
547 routine also does all the "intelligent" work like processing cpp
548 directives and so on. Note that often the routine is called
549 recursively and no cpp directives are printed. */
550 int cpp_read_line(gmx_cpp_t *handlep, int n, char buf[])
552 gmx_cpp_t handle = *handlep;
553 int status;
554 bool bEOF;
556 if (!handle)
558 return eCPP_INVALID_HANDLE;
560 if (!handle->fp)
562 return eCPP_FILE_NOT_OPEN;
565 bEOF = (feof(handle->fp) != 0);
566 if (!bEOF)
568 /* Read the actual line now. */
569 if (fgets2(buf, n-1, handle->fp) == nullptr)
571 /* Recheck EOF, since we could have been at the end before
572 * the fgets2 call, but we need to read past the end to know.
574 bEOF = (feof(handle->fp) != 0);
575 if (!bEOF)
577 /* Something strange happened, fgets returned NULL,
578 * but we are not at EOF.
580 return eCPP_UNKNOWN;
585 if (bEOF)
587 if (handle->parent == nullptr)
589 return eCPP_EOF;
591 cpp_close_file(handlep);
592 *handlep = handle->parent;
593 delete handle;
594 return cpp_read_line(handlep, n, buf);
596 else
598 handle->line = buf;
599 handle->line_nr++;
600 } /* Now we've read a line! */
602 /* Process directives if this line contains one */
603 std::string dname;
604 std::string dval;
605 if (find_directive(buf, &dname, &dval))
607 status = process_directive(handlep, dname, dval);
608 if (status != eCPP_OK)
610 return status;
612 /* Don't print lines with directives, go on to the next */
613 return cpp_read_line(handlep, n, buf);
616 /* Check whether we're not ifdeffed out. The order of this statement
617 is important. It has to come after #ifdef, #else and #endif, but
618 anything else should be ignored. */
619 if (is_ifdeffed_out(handle->ifdefs))
621 return cpp_read_line(handlep, n, buf);
624 /* Check whether we have any defines that need to be replaced. Note
625 that we have to use a best fit algorithm, rather than first come
626 first go. We do this by sorting the defines on length first, and
627 then on alphabetical order. */
628 for (t_define &define : *handle->defines)
630 if (!define.def.empty())
632 int nn = 0;
633 const char *ptr = buf;
634 while ((ptr = strstrw(ptr, define.name.c_str())) != nullptr)
636 nn++;
637 ptr += strlen(define.name.c_str());
639 if (nn > 0)
641 // Need to erase unmatched define in original handle
642 gmx_cpp_t root = handle;
643 while (root->parent != nullptr)
645 root = root->parent;
647 root->unmatched_defines.erase(define.name);
649 std::string name;
650 const char *ptr = buf;
651 const char *ptr2;
652 while ((ptr2 = strstrw(ptr, define.name.c_str())) != nullptr)
654 name.append(ptr, ptr2 - ptr);
655 name += define.def;
656 ptr = ptr2 + define.name.size();
658 name += ptr;
659 GMX_RELEASE_ASSERT(name.size() < static_cast<size_t>(n),
660 "The line should fit in buf");
661 strcpy(buf, name.c_str());
666 return eCPP_OK;
669 const char *cpp_cur_file(const gmx_cpp_t *handlep)
671 return (*handlep)->fn.c_str();
674 int cpp_cur_linenr(const gmx_cpp_t *handlep)
676 return (*handlep)->line_nr;
679 /* Close the file! Return integer status. */
680 int cpp_close_file(gmx_cpp_t *handlep)
682 gmx_cpp_t handle = *handlep;
684 if (!handle)
686 return eCPP_INVALID_HANDLE;
688 if (!handle->fp)
690 return eCPP_FILE_NOT_OPEN;
692 fclose(handle->fp);
694 if (!handle->cwd.empty())
696 gmx_chdir(handle->cwd.c_str());
699 handle->fp = nullptr;
700 handle->line_nr = 0;
701 handle->line.clear();
703 return eCPP_OK;
706 const std::string *cpp_find_define(const gmx_cpp_t *handlep,
707 const std::string &defineName)
709 for (const t_define &define : *(*handlep)->defines)
711 if (define.name == defineName)
713 return &define.def;
717 return nullptr;
720 void cpp_done(gmx_cpp_t handle)
722 int status = cpp_close_file(&handle);
723 if (status != eCPP_OK)
725 gmx_fatal(FARGS, "%s", cpp_error(&handle, status));
727 delete handle;
730 /* Return a string containing the error message coresponding to status
731 variable */
732 char *cpp_error(gmx_cpp_t *handlep, int status)
734 char buf[256];
735 const char *ecpp[] = {
736 "OK", "File not found", "End of file", "Syntax error", "Interrupted",
737 "Invalid file handle",
738 "File not open", "Unknown error", "Error status out of range"
740 gmx_cpp_t handle = *handlep;
742 if (!handle)
744 return const_cast<char *>(ecpp[eCPP_INVALID_HANDLE]);
747 if ((status < 0) || (status >= eCPP_NR))
749 status = eCPP_NR;
752 sprintf(buf, "%s - File %s, line %d\nLast line read:\n'%s'",
753 ecpp[status],
754 (handle && !handle->fn.empty()) ? handle->fn.c_str() : "unknown",
755 (handle) ? handle->line_nr : -1,
756 !handle->line.empty() ? handle->line.c_str() : "");
758 return gmx_strdup(buf);
761 std::string checkAndWarnForUnusedDefines(const gmx_cpp &handle)
763 std::string warning;
764 if (!handle.unmatched_defines.empty())
766 warning = "The following macros were defined in the 'define' mdp field with the -D prefix, but "
767 "were not used in the topology:\n";
768 for (auto &str : handle.unmatched_defines)
770 warning += (" " + str + "\n");
772 warning += "If you haven't made a spelling error, either use the macro you defined, "
773 "or don't define the macro";
775 return warning;