Add replacements for pbc enumerations
[gromacs.git] / src / gromacs / gmxpreprocess / gmxcpp.cpp
blobd232b55dbf109e4f0aae681c6b3cc9999a114990
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 // An include needs to be followed by either a '"' or a '<' as a first character.
457 if ((dval[0] != '"') && (dval[0] != '<'))
459 return eCPP_INVALID_INCLUDE_DELIMITER;
461 for (size_t i1 = 0; i1 < dval.size(); i1++)
463 if ((dval[i1] == '"') || (dval[i1] == '<') || (dval[i1] == '>'))
465 if (len == -1)
467 i0 = i1+1;
468 len = 0;
470 else
472 break;
475 else if (len >= 0)
477 len++;
480 if (len == -1)
482 return eCPP_SYNTAX;
484 std::string inc_fn = dval.substr(i0, len);
486 /* Open include file and store it as a child in the handle structure */
487 int status = cpp_open_file(inc_fn.c_str(), &(handle->child), nullptr,
488 &handle->defines, &handle->includes);
489 if (status != eCPP_OK)
491 handle->child = nullptr;
492 return status;
494 /* Make a linked list of open files and move on to the include file */
495 handle->child->parent = handle;
496 *handlep = handle->child;
497 return eCPP_OK;
500 /* #define statement */
501 if (dname == "define")
503 // A bare '#define' is an invalid line
504 if (dval.empty())
506 return eCPP_SYNTAX;
508 /* Split it into name and value. */
509 const char *ptr = dval.c_str();
510 while ((*ptr != '\0') && !isspace(*ptr))
512 ptr++;
514 std::string name = dval.substr(0, ptr - dval.c_str());
516 while ((*ptr != '\0') && isspace(*ptr))
518 ptr++;
521 add_define(handle->defines.get(), name, ptr);
522 return eCPP_OK;
525 /* #undef statement */
526 if (dname == "undef")
528 // A bare '#undef' is an invalid line
529 if (dval.empty())
531 return eCPP_SYNTAX;
533 std::vector<t_define> &defines = *handle->defines;
534 for (size_t i = 0; i < defines.size(); i++)
536 if (defines[i].name == dval)
538 defines.erase(defines.begin() + i);
539 break;
543 return eCPP_OK;
546 /* If we haven't matched anything, this is an unknown directive */
547 return eCPP_SYNTAX;
550 /* Return one whole line from the file into buf which holds at most n
551 characters, for subsequent processing. Returns integer status. This
552 routine also does all the "intelligent" work like processing cpp
553 directives and so on. Note that often the routine is called
554 recursively and no cpp directives are printed. */
555 int cpp_read_line(gmx_cpp_t *handlep, int n, char buf[])
557 gmx_cpp_t handle = *handlep;
558 int status;
559 bool bEOF;
561 if (!handle)
563 return eCPP_INVALID_HANDLE;
565 if (!handle->fp)
567 return eCPP_FILE_NOT_OPEN;
570 bEOF = (feof(handle->fp) != 0);
571 if (!bEOF)
573 /* Read the actual line now. */
574 if (fgets2(buf, n-1, handle->fp) == nullptr)
576 /* Recheck EOF, since we could have been at the end before
577 * the fgets2 call, but we need to read past the end to know.
579 bEOF = (feof(handle->fp) != 0);
580 if (!bEOF)
582 /* Something strange happened, fgets returned NULL,
583 * but we are not at EOF. Maybe wrong line endings?
585 return eCPP_UNKNOWN;
590 if (bEOF)
592 if (handle->parent == nullptr)
594 return eCPP_EOF;
596 cpp_close_file(handlep);
597 *handlep = handle->parent;
598 delete handle;
599 return cpp_read_line(handlep, n, buf);
601 else
603 handle->line = buf;
604 handle->line_nr++;
605 } /* Now we've read a line! */
607 /* Process directives if this line contains one */
608 std::string dname;
609 std::string dval;
610 if (find_directive(buf, &dname, &dval))
612 status = process_directive(handlep, dname, dval);
613 if (status != eCPP_OK)
615 return status;
617 /* Don't print lines with directives, go on to the next */
618 return cpp_read_line(handlep, n, buf);
621 /* Check whether we're not ifdeffed out. The order of this statement
622 is important. It has to come after #ifdef, #else and #endif, but
623 anything else should be ignored. */
624 if (is_ifdeffed_out(handle->ifdefs))
626 return cpp_read_line(handlep, n, buf);
629 /* Check whether we have any defines that need to be replaced. Note
630 that we have to use a best fit algorithm, rather than first come
631 first go. We do this by sorting the defines on length first, and
632 then on alphabetical order. */
633 for (t_define &define : *handle->defines)
635 if (!define.def.empty())
637 int nn = 0;
638 const char *ptr = buf;
639 while ((ptr = strstrw(ptr, define.name.c_str())) != nullptr)
641 nn++;
642 ptr += strlen(define.name.c_str());
644 if (nn > 0)
646 // Need to erase unmatched define in original handle
647 gmx_cpp_t root = handle;
648 while (root->parent != nullptr)
650 root = root->parent;
652 root->unmatched_defines.erase(define.name);
654 std::string name;
655 const char *ptr = buf;
656 const char *ptr2;
657 while ((ptr2 = strstrw(ptr, define.name.c_str())) != nullptr)
659 name.append(ptr, ptr2 - ptr);
660 name += define.def;
661 ptr = ptr2 + define.name.size();
663 name += ptr;
664 GMX_RELEASE_ASSERT(name.size() < static_cast<size_t>(n),
665 "The line should fit in buf");
666 strcpy(buf, name.c_str());
671 return eCPP_OK;
674 const char *cpp_cur_file(const gmx_cpp_t *handlep)
676 return (*handlep)->fn.c_str();
679 int cpp_cur_linenr(const gmx_cpp_t *handlep)
681 return (*handlep)->line_nr;
684 /* Close the file! Return integer status. */
685 int cpp_close_file(gmx_cpp_t *handlep)
687 gmx_cpp_t handle = *handlep;
689 if (!handle)
691 return eCPP_INVALID_HANDLE;
693 if (!handle->fp)
695 return eCPP_FILE_NOT_OPEN;
697 fclose(handle->fp);
699 if (!handle->cwd.empty())
701 gmx_chdir(handle->cwd.c_str());
704 handle->fp = nullptr;
705 handle->line_nr = 0;
706 handle->line.clear();
708 return eCPP_OK;
711 const std::string *cpp_find_define(const gmx_cpp_t *handlep,
712 const std::string &defineName)
714 for (const t_define &define : *(*handlep)->defines)
716 if (define.name == defineName)
718 return &define.def;
722 return nullptr;
725 void cpp_done(gmx_cpp_t handle)
727 int status = cpp_close_file(&handle);
728 if (status != eCPP_OK)
730 gmx_fatal(FARGS, "%s", cpp_error(&handle, status));
732 delete handle;
735 /* Return a string containing the error message coresponding to status
736 variable */
737 char *cpp_error(gmx_cpp_t *handlep, int status)
739 char buf[256];
740 const char *ecpp[] = {
741 "OK", "File not found", "End of file", "Syntax error", "Interrupted",
742 "Invalid file handle", "Invalid delimiter for filename in #include statement",
743 "File not open", "Unknown error, perhaps your text file uses wrong line endings?", "Error status out of range"
745 gmx_cpp_t handle = *handlep;
747 if (!handle)
749 return const_cast<char *>(ecpp[eCPP_INVALID_HANDLE]);
752 if ((status < 0) || (status >= eCPP_NR))
754 status = eCPP_NR;
757 sprintf(buf, "%s - File %s, line %d\nLast line read:\n'%s'",
758 ecpp[status],
759 (handle && !handle->fn.empty()) ? handle->fn.c_str() : "unknown",
760 (handle) ? handle->line_nr : -1,
761 !handle->line.empty() ? handle->line.c_str() : "");
763 return gmx_strdup(buf);
766 std::string checkAndWarnForUnusedDefines(const gmx_cpp &handle)
768 std::string warning;
769 if (!handle.unmatched_defines.empty())
771 warning = "The following macros were defined in the 'define' mdp field with the -D prefix, but "
772 "were not used in the topology:\n";
773 for (auto &str : handle.unmatched_defines)
775 warning += (" " + str + "\n");
777 warning += "If you haven't made a spelling error, either use the macro you defined, "
778 "or don't define the macro";
780 return warning;