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 by the GROMACS development team.
7 * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
52 #include <unordered_set>
54 #include <sys/types.h>
56 #include "gromacs/utility/arrayref.h"
57 #include "gromacs/utility/cstringutil.h"
58 #include "gromacs/utility/dir_separator.h"
59 #include "gromacs/utility/fatalerror.h"
60 #include "gromacs/utility/futil.h"
61 #include "gromacs/utility/gmxassert.h"
69 /* enum used for handling ifdefs */
80 std::shared_ptr<std::vector<t_define>> defines;
81 std::shared_ptr<std::vector<std::string>> includes;
82 std::unordered_set<std::string> unmatched_defines;
89 std::vector<int> ifdefs;
90 struct gmx_cpp* child = nullptr;
91 struct gmx_cpp* parent = nullptr;
94 static bool is_word_end(char c)
96 return !((isalnum(c) != 0) || c == '_');
99 static const char* strstrw(const char* buf, const char* word)
103 while ((ptr = strstr(buf, word)) != nullptr)
105 /* Check if we did not find part of a longer word */
106 if (ptr && is_word_end(ptr[strlen(word)])
107 && (((ptr > buf) && is_word_end(ptr[-1])) || (ptr == buf)))
112 buf = ptr + strlen(word);
117 /* Finds a preprocessor directive, whose name (after the '#') is
118 * returned in *name, and the remainder of the line after leading
119 * whitespace, without trailing whitespace, is returned in *val
121 static bool find_directive(const char* buf, std::string* name, std::string* val)
123 /* Skip initial whitespace */
124 while (isspace(*buf))
128 /* Check if this is a directive */
133 /* Skip the hash and any space after it */
135 while (isspace(*buf))
139 /* Set the name pointer and find the next space */
141 while (*buf != '\0' && !isspace(*buf))
146 /* Set the end of the name here, and skip any space */
150 while (isspace(*buf))
155 /* Check if anything is remaining */
159 // Remove trailing whitespace
160 while (!val->empty() && isspace(val->back()))
162 val->resize(val->size() - 1);
173 static bool is_ifdeffed_out(gmx::ArrayRef<const int> ifdefs)
175 return (!ifdefs.empty() && ifdefs.back() != eifTRUE);
178 static void add_include(std::vector<std::string>* includes, const char* includePath)
180 GMX_RELEASE_ASSERT(includes, "Need valid includes");
181 GMX_RELEASE_ASSERT(includePath, "Need a valid include path");
183 for (const std::string& include : *includes)
185 if (strcmp(include.c_str(), includePath) == 0)
191 includes->push_back(includePath);
194 static void add_define(std::vector<t_define>* defines, const std::string& name, const char* value)
196 GMX_RELEASE_ASSERT(defines, "Need defines");
197 GMX_RELEASE_ASSERT(value, "Need a value");
199 for (t_define& define : *defines)
201 if (define.name == name)
208 defines->push_back({ name, value });
211 /* Open the file to be processed. The handle variable holds internal
212 info for the cpp emulator. Return integer status */
213 static int cpp_open_file(const char* filenm,
216 std::shared_ptr<std::vector<t_define>>* definesFromParent,
217 std::shared_ptr<std::vector<std::string>>* includesFromParent)
219 // TODO: We should avoid new/delete, we should use Pimpl instead
220 gmx_cpp* cpp = new gmx_cpp;
223 if (definesFromParent)
225 cpp->defines = *definesFromParent;
229 cpp->defines = std::make_shared<std::vector<t_define>>();
232 if (includesFromParent)
234 cpp->includes = *includesFromParent;
238 cpp->includes = std::make_shared<std::vector<std::string>>();
241 /* First process options, they might be necessary for opening files
242 (especially include statements). */
248 if (strstr(cppopts[i], "-I") == cppopts[i])
250 add_include(cpp->includes.get(), cppopts[i] + 2);
252 if (strstr(cppopts[i], "-D") == cppopts[i])
254 /* If the option contains a =, split it into name and value. */
255 char* ptr = strchr(cppopts[i], '=');
258 std::string buf = cppopts[i] + 2;
259 buf.resize(ptr - cppopts[i] - 2);
260 add_define(cpp->defines.get(), buf, ptr + 1);
261 cpp->unmatched_defines.insert(buf);
265 add_define(cpp->defines.get(), cppopts[i] + 2, "");
266 cpp->unmatched_defines.insert(cppopts[i] + 2);
273 /* Find the file. First check whether it is in the current directory. */
274 if (gmx_fexist(filenm))
280 /* If not, check all the paths given with -I. */
281 for (const std::string& include : *cpp->includes)
283 std::string buf = include + "/" + filenm;
290 /* If still not found, check the Gromacs library search path. */
293 cpp->fn = gmx::findLibraryFile(filenm, false, false);
298 gmx_fatal(FARGS, "Topology include file \"%s\" not found", filenm);
300 /* If the file name has a path component, we need to change to that
301 * directory. Note that we - just as C - always use UNIX path separators
302 * internally in include file names.
304 size_t pos = cpp->fn.rfind('/');
305 size_t pos2 = cpp->fn.rfind(DIR_SEPARATOR);
307 if (pos == std::string::npos || (pos2 != std::string::npos && pos2 > pos))
311 if (pos != std::string::npos)
314 cpp->path.resize(pos);
315 cpp->fn.erase(0, pos + 1);
318 gmx_getcwd(buf, STRLEN);
321 gmx_chdir(cpp->path.c_str());
326 cpp->child = nullptr;
327 cpp->parent = nullptr;
328 if (cpp->fp == nullptr)
330 cpp->fp = fopen(cpp->fn.c_str(), "r");
332 if (cpp->fp == nullptr)
337 default: return eCPP_UNKNOWN;
343 /* Open the file to be processed. The handle variable holds internal
344 info for the cpp emulator. Return integer status */
345 int cpp_open_file(const char* filenm, gmx_cpp_t* handle, char** cppopts)
347 return cpp_open_file(filenm, handle, cppopts, nullptr, nullptr);
350 /* Note that dval might be null, e.g. when handling a line like '#define */
351 static int process_directive(gmx_cpp_t* handlep, const std::string& dname, const std::string& dval)
353 gmx_cpp_t handle = *handlep;
355 std::vector<int>& ifdefs = handle->ifdefs;
357 /* #ifdef or ifndef statement */
358 bool bIfdef = (dname == "ifdef");
359 bool bIfndef = (dname == "ifndef");
360 if (bIfdef || bIfndef)
362 if (is_ifdeffed_out(ifdefs))
364 handle->ifdefs.push_back(eifIGNORE);
368 // A bare '#ifdef' or '#ifndef' is invalid
374 for (const t_define& define : *handle->defines)
376 if (define.name == dval)
378 // erase from unmatched_defines in original handle
379 gmx_cpp_t root = handle;
380 while (root->parent != nullptr)
384 root->unmatched_defines.erase(dval);
390 if ((bIfdef && found) || (bIfndef && !found))
392 ifdefs.push_back(eifTRUE);
396 ifdefs.push_back(eifFALSE);
402 /* #else statement */
409 if (ifdefs.back() == eifTRUE)
411 ifdefs.back() = eifFALSE;
413 else if (ifdefs.back() == eifFALSE)
415 ifdefs.back() = eifTRUE;
420 /* #endif statement */
421 if (dname == "endif")
427 ifdefs.erase(ifdefs.end() - 1);
431 /* Check whether we're not ifdeffed out. The order of this statement
432 is important. It has to come after #ifdef, #else and #endif, but
433 anything else should be ignored. */
434 if (is_ifdeffed_out(ifdefs))
439 /* Check for include statements */
440 if (dname == "include")
444 // A bare '#include' is an invalid line
449 // An include needs to be followed by either a '"' or a '<' as a first character.
450 if ((dval[0] != '"') && (dval[0] != '<'))
452 return eCPP_INVALID_INCLUDE_DELIMITER;
454 for (size_t i1 = 0; i1 < dval.size(); i1++)
456 if ((dval[i1] == '"') || (dval[i1] == '<') || (dval[i1] == '>'))
477 std::string inc_fn = dval.substr(i0, len);
479 /* Open include file and store it as a child in the handle structure */
480 int status = cpp_open_file(
481 inc_fn.c_str(), &(handle->child), nullptr, &handle->defines, &handle->includes);
482 if (status != eCPP_OK)
484 handle->child = nullptr;
487 /* Make a linked list of open files and move on to the include file */
488 handle->child->parent = handle;
489 *handlep = handle->child;
493 /* #define statement */
494 if (dname == "define")
496 // A bare '#define' is an invalid line
501 /* Split it into name and value. */
502 const char* ptr = dval.c_str();
503 while ((*ptr != '\0') && !isspace(*ptr))
507 std::string name = dval.substr(0, ptr - dval.c_str());
509 while ((*ptr != '\0') && isspace(*ptr))
514 add_define(handle->defines.get(), name, ptr);
518 /* #undef statement */
519 if (dname == "undef")
521 // A bare '#undef' is an invalid line
526 std::vector<t_define>& defines = *handle->defines;
527 for (size_t i = 0; i < defines.size(); i++)
529 if (defines[i].name == dval)
531 defines.erase(defines.begin() + i);
539 /* If we haven't matched anything, this is an unknown directive */
543 /* Return one whole line from the file into buf which holds at most n
544 characters, for subsequent processing. Returns integer status. This
545 routine also does all the "intelligent" work like processing cpp
546 directives and so on. Note that often the routine is called
547 recursively and no cpp directives are printed. */
548 int cpp_read_line(gmx_cpp_t* handlep, int n, char buf[])
550 gmx_cpp_t handle = *handlep;
556 return eCPP_INVALID_HANDLE;
560 return eCPP_FILE_NOT_OPEN;
563 bEOF = (feof(handle->fp) != 0);
566 /* Read the actual line now. */
567 if (fgets2(buf, n - 1, handle->fp) == nullptr)
569 /* Recheck EOF, since we could have been at the end before
570 * the fgets2 call, but we need to read past the end to know.
572 bEOF = (feof(handle->fp) != 0);
575 /* Something strange happened, fgets returned NULL,
576 * but we are not at EOF. Maybe wrong line endings?
585 if (handle->parent == nullptr)
589 cpp_close_file(handlep);
590 *handlep = handle->parent;
592 return cpp_read_line(handlep, n, buf);
598 } /* Now we've read a line! */
600 /* Process directives if this line contains one */
603 if (find_directive(buf, &dname, &dval))
605 status = process_directive(handlep, dname, dval);
606 if (status != eCPP_OK)
610 /* Don't print lines with directives, go on to the next */
611 return cpp_read_line(handlep, n, buf);
614 /* Check whether we're not ifdeffed out. The order of this statement
615 is important. It has to come after #ifdef, #else and #endif, but
616 anything else should be ignored. */
617 if (is_ifdeffed_out(handle->ifdefs))
619 return cpp_read_line(handlep, n, buf);
622 /* Check whether we have any defines that need to be replaced. Note
623 that we have to use a best fit algorithm, rather than first come
624 first go. We do this by sorting the defines on length first, and
625 then on alphabetical order. */
626 for (t_define& define : *handle->defines)
628 if (!define.def.empty())
631 const char* ptr = buf;
632 while ((ptr = strstrw(ptr, define.name.c_str())) != nullptr)
635 ptr += strlen(define.name.c_str());
639 // Need to erase unmatched define in original handle
640 gmx_cpp_t root = handle;
641 while (root->parent != nullptr)
645 root->unmatched_defines.erase(define.name);
648 const char* ptr = buf;
650 while ((ptr2 = strstrw(ptr, define.name.c_str())) != nullptr)
652 name.append(ptr, ptr2 - ptr);
654 ptr = ptr2 + define.name.size();
657 GMX_RELEASE_ASSERT(name.size() < static_cast<size_t>(n),
658 "The line should fit in buf");
659 strcpy(buf, name.c_str());
667 const char* cpp_cur_file(const gmx_cpp_t* handlep)
669 return (*handlep)->fn.c_str();
672 int cpp_cur_linenr(const gmx_cpp_t* handlep)
674 return (*handlep)->line_nr;
677 /* Close the file! Return integer status. */
678 int cpp_close_file(gmx_cpp_t* handlep)
680 gmx_cpp_t handle = *handlep;
684 return eCPP_INVALID_HANDLE;
688 return eCPP_FILE_NOT_OPEN;
692 if (!handle->cwd.empty())
694 gmx_chdir(handle->cwd.c_str());
697 handle->fp = nullptr;
699 handle->line.clear();
704 const std::string* cpp_find_define(const gmx_cpp_t* handlep, const std::string& defineName)
706 for (const t_define& define : *(*handlep)->defines)
708 if (define.name == defineName)
717 void cpp_done(gmx_cpp_t handle)
719 int status = cpp_close_file(&handle);
720 if (status != eCPP_OK)
722 gmx_fatal(FARGS, "%s", cpp_error(&handle, status));
727 /* Return a string containing the error message coresponding to status
729 char* cpp_error(gmx_cpp_t* handlep, int status)
732 const char* ecpp[] = { "OK",
737 "Invalid file handle",
738 "Invalid delimiter for filename in #include statement",
740 "Unknown error, perhaps your text file uses wrong line endings?",
741 "Error status out of range" };
742 gmx_cpp_t handle = *handlep;
746 return const_cast<char*>(ecpp[eCPP_INVALID_HANDLE]);
749 if ((status < 0) || (status >= eCPP_NR))
755 "%s - File %s, line %d\nLast line read:\n'%s'",
757 (handle && !handle->fn.empty()) ? handle->fn.c_str() : "unknown",
758 (handle) ? handle->line_nr : -1,
759 !handle->line.empty() ? handle->line.c_str() : "");
761 return gmx_strdup(buf);
764 std::string checkAndWarnForUnusedDefines(const gmx_cpp& handle)
767 if (!handle.unmatched_defines.empty())
770 "The following macros were defined in the 'define' mdp field with the -D prefix, "
772 "were not used in the topology:\n";
773 for (const auto& str : handle.unmatched_defines)
775 warning += (" " + str + "\n");
778 "If you haven't made a spelling error, either use the macro you defined, "
779 "or don't define the macro";