Update instructions in containers.rst
[gromacs.git] / src / gromacs / gmxpreprocess / gmxcpp.cpp
blobc359c7a450851cbabcafe7686df08df1b98075ce
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 by the GROMACS development team.
7 * Copyright (c) 2019,2020, 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.
38 #include "gmxpre.h"
40 #include "gmxcpp.h"
42 #include <cctype>
43 #include <cerrno>
44 #include <climits>
45 #include <cmath>
46 #include <cstdio>
47 #include <cstdlib>
48 #include <cstring>
50 #include <algorithm>
51 #include <memory>
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"
63 struct t_define
65 std::string name;
66 std::string def;
69 /* enum used for handling ifdefs */
70 enum
72 eifTRUE,
73 eifFALSE,
74 eifIGNORE,
75 eifNR
78 struct gmx_cpp
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;
83 FILE* fp = nullptr;
84 std::string path;
85 std::string cwd;
86 std::string fn;
87 std::string line;
88 int line_nr;
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)
101 const char* ptr;
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)))
109 return ptr;
112 buf = ptr + strlen(word);
114 return nullptr;
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))
126 ++buf;
128 /* Check if this is a directive */
129 if (*buf != '#')
131 return FALSE;
133 /* Skip the hash and any space after it */
134 ++buf;
135 while (isspace(*buf))
137 ++buf;
139 /* Set the name pointer and find the next space */
140 name->clear();
141 while (*buf != '\0' && !isspace(*buf))
143 *name += *buf;
144 ++buf;
146 /* Set the end of the name here, and skip any space */
147 if (*buf != '\0')
149 ++buf;
150 while (isspace(*buf))
152 ++buf;
155 /* Check if anything is remaining */
156 if (*buf != '\0')
158 *val = buf;
159 // Remove trailing whitespace
160 while (!val->empty() && isspace(val->back()))
162 val->resize(val->size() - 1);
165 else
167 val->clear();
170 return TRUE;
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)
187 return;
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)
203 define.def = value;
204 return;
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,
214 gmx_cpp_t* handle,
215 char** cppopts,
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;
221 *handle = cpp;
223 if (definesFromParent)
225 cpp->defines = *definesFromParent;
227 else
229 cpp->defines = std::make_shared<std::vector<t_define>>();
232 if (includesFromParent)
234 cpp->includes = *includesFromParent;
236 else
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). */
243 int i = 0;
244 if (cppopts)
246 while (cppopts[i])
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], '=');
256 if (ptr)
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);
263 else
265 add_define(cpp->defines.get(), cppopts[i] + 2, "");
266 cpp->unmatched_defines.insert(cppopts[i] + 2);
269 i++;
273 /* Find the file. First check whether it is in the current directory. */
274 if (gmx_fexist(filenm))
276 cpp->fn = filenm;
278 else
280 /* If not, check all the paths given with -I. */
281 for (const std::string& include : *cpp->includes)
283 std::string buf = include + "/" + filenm;
284 if (gmx_fexist(buf))
286 cpp->fn = buf;
287 break;
290 /* If still not found, check the Gromacs library search path. */
291 if (cpp->fn.empty())
293 cpp->fn = gmx::findLibraryFile(filenm, false, false);
296 if (cpp->fn.empty())
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))
309 pos = pos2;
311 if (pos != std::string::npos)
313 cpp->path = cpp->fn;
314 cpp->path.resize(pos);
315 cpp->fn.erase(0, pos + 1);
317 char buf[STRLEN];
318 gmx_getcwd(buf, STRLEN);
319 cpp->cwd = buf;
321 gmx_chdir(cpp->path.c_str());
323 cpp->line.clear();
324 cpp->line_nr = 0;
325 cpp->ifdefs.clear();
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)
334 switch (errno)
336 case EINVAL:
337 default: return eCPP_UNKNOWN;
340 return eCPP_OK;
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);
366 else
368 // A bare '#ifdef' or '#ifndef' is invalid
369 if (dval.empty())
371 return eCPP_SYNTAX;
373 bool found = false;
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)
382 root = root->parent;
384 root->unmatched_defines.erase(dval);
386 found = true;
387 break;
390 if ((bIfdef && found) || (bIfndef && !found))
392 ifdefs.push_back(eifTRUE);
394 else
396 ifdefs.push_back(eifFALSE);
399 return eCPP_OK;
402 /* #else statement */
403 if (dname == "else")
405 if (ifdefs.empty())
407 return eCPP_SYNTAX;
409 if (ifdefs.back() == eifTRUE)
411 ifdefs.back() = eifFALSE;
413 else if (ifdefs.back() == eifFALSE)
415 ifdefs.back() = eifTRUE;
417 return eCPP_OK;
420 /* #endif statement */
421 if (dname == "endif")
423 if (ifdefs.empty())
425 return eCPP_SYNTAX;
427 ifdefs.erase(ifdefs.end() - 1);
428 return eCPP_OK;
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))
436 return eCPP_OK;
439 /* Check for include statements */
440 if (dname == "include")
442 int len = -1;
443 int i0 = 0;
444 // A bare '#include' is an invalid line
445 if (dval.empty())
447 return eCPP_SYNTAX;
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] == '>'))
458 if (len == -1)
460 i0 = i1 + 1;
461 len = 0;
463 else
465 break;
468 else if (len >= 0)
470 len++;
473 if (len == -1)
475 return eCPP_SYNTAX;
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(inc_fn.c_str(), &(handle->child), nullptr, &handle->defines,
481 &handle->includes);
482 if (status != eCPP_OK)
484 handle->child = nullptr;
485 return status;
487 /* Make a linked list of open files and move on to the include file */
488 handle->child->parent = handle;
489 *handlep = handle->child;
490 return eCPP_OK;
493 /* #define statement */
494 if (dname == "define")
496 // A bare '#define' is an invalid line
497 if (dval.empty())
499 return eCPP_SYNTAX;
501 /* Split it into name and value. */
502 const char* ptr = dval.c_str();
503 while ((*ptr != '\0') && !isspace(*ptr))
505 ptr++;
507 std::string name = dval.substr(0, ptr - dval.c_str());
509 while ((*ptr != '\0') && isspace(*ptr))
511 ptr++;
514 add_define(handle->defines.get(), name, ptr);
515 return eCPP_OK;
518 /* #undef statement */
519 if (dname == "undef")
521 // A bare '#undef' is an invalid line
522 if (dval.empty())
524 return eCPP_SYNTAX;
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);
532 break;
536 return eCPP_OK;
539 /* If we haven't matched anything, this is an unknown directive */
540 return eCPP_SYNTAX;
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;
551 int status;
552 bool bEOF;
554 if (!handle)
556 return eCPP_INVALID_HANDLE;
558 if (!handle->fp)
560 return eCPP_FILE_NOT_OPEN;
563 bEOF = (feof(handle->fp) != 0);
564 if (!bEOF)
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);
573 if (!bEOF)
575 /* Something strange happened, fgets returned NULL,
576 * but we are not at EOF. Maybe wrong line endings?
578 return eCPP_UNKNOWN;
583 if (bEOF)
585 if (handle->parent == nullptr)
587 return eCPP_EOF;
589 cpp_close_file(handlep);
590 *handlep = handle->parent;
591 delete handle;
592 return cpp_read_line(handlep, n, buf);
594 else
596 handle->line = buf;
597 handle->line_nr++;
598 } /* Now we've read a line! */
600 /* Process directives if this line contains one */
601 std::string dname;
602 std::string dval;
603 if (find_directive(buf, &dname, &dval))
605 status = process_directive(handlep, dname, dval);
606 if (status != eCPP_OK)
608 return status;
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())
630 int nn = 0;
631 const char* ptr = buf;
632 while ((ptr = strstrw(ptr, define.name.c_str())) != nullptr)
634 nn++;
635 ptr += strlen(define.name.c_str());
637 if (nn > 0)
639 // Need to erase unmatched define in original handle
640 gmx_cpp_t root = handle;
641 while (root->parent != nullptr)
643 root = root->parent;
645 root->unmatched_defines.erase(define.name);
647 std::string name;
648 const char* ptr = buf;
649 const char* ptr2;
650 while ((ptr2 = strstrw(ptr, define.name.c_str())) != nullptr)
652 name.append(ptr, ptr2 - ptr);
653 name += define.def;
654 ptr = ptr2 + define.name.size();
656 name += ptr;
657 GMX_RELEASE_ASSERT(name.size() < static_cast<size_t>(n),
658 "The line should fit in buf");
659 strcpy(buf, name.c_str());
664 return eCPP_OK;
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;
682 if (!handle)
684 return eCPP_INVALID_HANDLE;
686 if (!handle->fp)
688 return eCPP_FILE_NOT_OPEN;
690 fclose(handle->fp);
692 if (!handle->cwd.empty())
694 gmx_chdir(handle->cwd.c_str());
697 handle->fp = nullptr;
698 handle->line_nr = 0;
699 handle->line.clear();
701 return eCPP_OK;
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)
710 return &define.def;
714 return nullptr;
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));
724 delete handle;
727 /* Return a string containing the error message coresponding to status
728 variable */
729 char* cpp_error(gmx_cpp_t* handlep, int status)
731 char buf[256];
732 const char* ecpp[] = { "OK",
733 "File not found",
734 "End of file",
735 "Syntax error",
736 "Interrupted",
737 "Invalid file handle",
738 "Invalid delimiter for filename in #include statement",
739 "File not open",
740 "Unknown error, perhaps your text file uses wrong line endings?",
741 "Error status out of range" };
742 gmx_cpp_t handle = *handlep;
744 if (!handle)
746 return const_cast<char*>(ecpp[eCPP_INVALID_HANDLE]);
749 if ((status < 0) || (status >= eCPP_NR))
751 status = eCPP_NR;
754 sprintf(buf, "%s - File %s, line %d\nLast line read:\n'%s'", ecpp[status],
755 (handle && !handle->fn.empty()) ? handle->fn.c_str() : "unknown",
756 (handle) ? handle->line_nr : -1, !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 =
767 "The following macros were defined in the 'define' mdp field with the -D prefix, "
768 "but "
769 "were not used in the topology:\n";
770 for (auto& str : handle.unmatched_defines)
772 warning += (" " + str + "\n");
774 warning +=
775 "If you haven't made a spelling error, either use the macro you defined, "
776 "or don't define the macro";
778 return warning;