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, 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.
52 #include <sys/types.h>
54 #include "gromacs/utility/cstringutil.h"
55 #include "gromacs/utility/dir_separator.h"
56 #include "gromacs/utility/fatalerror.h"
57 #include "gromacs/utility/futil.h"
58 #include "gromacs/utility/gmxassert.h"
59 #include "gromacs/utility/smalloc.h"
67 static t_define *defs = nullptr;
69 static char **incl = nullptr;
71 /* enum used for handling ifdefs */
73 eifTRUE, eifFALSE, eifIGNORE, eifNR
76 typedef struct gmx_cpp {
85 struct gmx_cpp *child, *parent;
88 static bool is_word_end(char c)
90 return !(isalnum(c) || c == '_');
93 static const char *strstrw(const char *buf, const char *word)
97 while ((ptr = strstr(buf, word)) != nullptr)
99 /* Check if we did not find part of a longer word */
101 is_word_end(ptr[strlen(word)]) &&
102 (((ptr > buf) && is_word_end(ptr[-1])) || (ptr == buf)))
107 buf = ptr + strlen(word);
112 /* Finds a preprocessor directive, whose name (after the '#') is
113 * returned in *name, and the remainder of the line after leading
114 * whitespace is returned in *val (which can be nullptr). */
115 static bool find_directive(char *buf, char **name, char **val)
117 /* Skip initial whitespace */
118 while (isspace(*buf))
122 /* Check if this is a directive */
127 /* Skip the hash and any space after it */
129 while (isspace(*buf))
133 /* Set the name pointer and find the next space */
135 while (*buf != 0 && !isspace(*buf))
139 /* Set the end of the name here, and skip any space */
144 while (isspace(*buf))
149 /* Check if anything is remaining */
150 *val = (*buf != 0) ? buf : nullptr;
154 static bool is_ifdeffed_out(gmx_cpp_t handle)
156 return ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE));
159 static void add_include(const char *include)
163 if (include == nullptr)
168 for (i = 0; (i < nincl); i++)
170 if (strcmp(incl[i], include) == 0)
179 incl[nincl-1] = gmx_strdup(include);
183 static void done_includes()
186 for (i = 0; (i < nincl); i++)
195 static void add_define(const char *name, const char *value)
199 for (i = 0; (i < ndef); i++)
201 if (strcmp(defs[i].name, name) == 0)
211 defs[i].name = gmx_strdup(name);
213 else if (defs[i].def)
217 if (value && strlen(value) > 0)
219 defs[i].def = gmx_strdup(value);
223 defs[i].def = nullptr;
227 static void done_defines()
230 for (i = 0; (i < ndef); i++)
240 /* Open the file to be processed. The handle variable holds internal
241 info for the cpp emulator. Return integer status */
242 int cpp_open_file(const char *filenm, gmx_cpp_t *handle, char **cppopts)
249 /* First process options, they might be necessary for opening files
250 (especially include statements). */
256 if (strstr(cppopts[i], "-I") == cppopts[i])
258 add_include(cppopts[i]+2);
260 if (strstr(cppopts[i], "-D") == cppopts[i])
262 /* If the option contains a =, split it into name and value. */
263 ptr = strchr(cppopts[i], '=');
266 buf = gmx_strndup(cppopts[i] + 2, ptr - cppopts[i] - 2);
267 add_define(buf, ptr + 1);
272 add_define(cppopts[i] + 2, nullptr);
282 /* Find the file. First check whether it is in the current directory. */
283 if (gmx_fexist(filenm))
285 cpp->fn = gmx_strdup(filenm);
289 /* If not, check all the paths given with -I. */
290 for (i = 0; i < nincl; ++i)
292 snew(buf, strlen(incl[i]) + strlen(filenm) + 2);
293 sprintf(buf, "%s/%s", incl[i], filenm);
301 /* If still not found, check the Gromacs library search path. */
304 cpp->fn = low_gmxlibfn(filenm, FALSE, FALSE);
309 gmx_fatal(FARGS, "Topology include file \"%s\" not found", filenm);
311 /* If the file name has a path component, we need to change to that
312 * directory. Note that we - just as C - always use UNIX path separators
313 * internally in include file names.
315 ptr = strrchr(cpp->fn, '/');
316 ptr2 = strrchr(cpp->fn, DIR_SEPARATOR);
318 if (ptr == nullptr || (ptr2 != nullptr && ptr2 > ptr))
331 cpp->fn = gmx_strdup(ptr+1);
332 snew(cpp->cwd, STRLEN);
334 gmx_getcwd(cpp->cwd, STRLEN);
335 gmx_chdir(cpp->path);
341 cpp->ifdefs = nullptr;
342 cpp->child = nullptr;
343 cpp->parent = nullptr;
344 if (cpp->fp == nullptr)
346 cpp->fp = fopen(cpp->fn, "r");
348 if (cpp->fp == nullptr)
360 /* Note that dval might be null, e.g. when handling a line like '#define */
362 process_directive(gmx_cpp_t *handlep, const char *dname, const char *dval)
364 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
365 int i, i0, len, status;
371 /* #ifdef or ifndef statement */
372 bIfdef = (strcmp(dname, "ifdef") == 0);
373 bIfndef = (strcmp(dname, "ifndef") == 0);
374 if (bIfdef || bIfndef)
376 GMX_RELEASE_ASSERT(dval, "#ifdef/#ifndef requires an argument");
377 if ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE))
380 srenew(handle->ifdefs, handle->nifdef);
381 handle->ifdefs[handle->nifdef-1] = eifIGNORE;
385 // A bare '#ifdef' or '#ifndef' is invalid
390 snew(name, strlen(dval)+1);
391 sscanf(dval, "%s", name);
392 for (i = 0; (i < ndef); i++)
394 if (strcmp(defs[i].name, name) == 0)
400 srenew(handle->ifdefs, handle->nifdef);
401 if ((bIfdef && (i < ndef)) || (bIfndef && (i == ndef)))
403 handle->ifdefs[handle->nifdef-1] = eifTRUE;
407 handle->ifdefs[handle->nifdef-1] = eifFALSE;
414 /* #else statement */
415 if (strcmp(dname, "else") == 0)
417 if (handle->nifdef <= 0)
421 if (handle->ifdefs[handle->nifdef-1] == eifTRUE)
423 handle->ifdefs[handle->nifdef-1] = eifFALSE;
425 else if (handle->ifdefs[handle->nifdef-1] == eifFALSE)
427 handle->ifdefs[handle->nifdef-1] = eifTRUE;
432 /* #endif statement */
433 if (strcmp(dname, "endif") == 0)
435 if (handle->nifdef <= 0)
443 /* Check whether we're not ifdeffed out. The order of this statement
444 is important. It has to come after #ifdef, #else and #endif, but
445 anything else should be ignored. */
446 if (is_ifdeffed_out(handle))
451 /* Check for include statements */
452 if (strcmp(dname, "include") == 0)
454 GMX_RELEASE_ASSERT(dval, "#include requires an argument");
457 // A bare '#include' is an invalid line
462 for (i1 = 0; (i1 < strlen(dval)); i1++)
464 if ((dval[i1] == '"') || (dval[i1] == '<') || (dval[i1] == '>'))
486 strncpy(inc_fn, dval+i0, len);
489 /* Open include file and store it as a child in the handle structure */
490 status = cpp_open_file(inc_fn, &(handle->child), nullptr);
492 if (status != eCPP_OK)
494 handle->child = nullptr;
497 /* Make a linked list of open files and move on to the include file */
498 handle->child->parent = handle;
499 *handlep = handle->child;
503 /* #define statement */
504 if (strcmp(dname, "define") == 0)
506 // A bare '#define' is an invalid line
511 /* Split it into name and value. */
513 while ((*ptr != '\0') && !isspace(*ptr))
517 name = gmx_strndup(dval, ptr - dval);
519 while ((*ptr != '\0') && isspace(*ptr))
524 add_define(name, ptr);
529 /* #undef statement */
530 if (strcmp(dname, "undef") == 0)
532 // A bare '#undef' is an invalid line
537 snew(name, strlen(dval)+1);
538 sscanf(dval, "%s", name);
539 for (i = 0; (i < ndef); i++)
541 if (strcmp(defs[i].name, name) == 0)
549 for (; (i < ndef-1); i++)
551 defs[i].name = defs[i+1].name;
552 defs[i].def = defs[i+1].def;
559 /* If we haven't matched anything, this is an unknown directive */
563 /* Return one whole line from the file into buf which holds at most n
564 characters, for subsequent processing. Returns integer status. This
565 routine also does all the "intelligent" work like processing cpp
566 directives and so on. Note that often the routine is called
567 recursively and no cpp directives are printed. */
568 int cpp_read_line(gmx_cpp_t *handlep, int n, char buf[])
570 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
571 int i, nn, len, status;
572 const char *ptr, *ptr2;
579 return eCPP_INVALID_HANDLE;
583 return eCPP_FILE_NOT_OPEN;
586 bEOF = feof(handle->fp);
589 /* Read the actual line now. */
590 if (fgets2(buf, n-1, handle->fp) == nullptr)
592 /* Recheck EOF, since we could have been at the end before
593 * the fgets2 call, but we need to read past the end to know.
595 bEOF = feof(handle->fp);
598 /* Something strange happened, fgets returned NULL,
599 * but we are not at EOF.
608 if (handle->parent == nullptr)
612 cpp_close_file(handlep);
613 *handlep = handle->parent;
614 handle->child = nullptr;
615 return cpp_read_line(handlep, n, buf);
619 if (n > handle->line_len)
621 handle->line_len = n;
622 srenew(handle->line, n);
624 strcpy(handle->line, buf);
626 } /* Now we've read a line! */
628 /* Process directives if this line contains one */
629 if (find_directive(buf, &dname, &dval))
631 status = process_directive(handlep, dname, dval);
632 if (status != eCPP_OK)
636 /* Don't print lines with directives, go on to the next */
637 return cpp_read_line(handlep, n, buf);
640 /* Check whether we're not ifdeffed out. The order of this statement
641 is important. It has to come after #ifdef, #else and #endif, but
642 anything else should be ignored. */
643 if (is_ifdeffed_out(handle))
645 return cpp_read_line(handlep, n, buf);
648 /* Check whether we have any defines that need to be replaced. Note
649 that we have to use a best fit algorithm, rather than first come
650 first go. We do this by sorting the defines on length first, and
651 then on alphabetical order. */
652 for (i = 0; (i < ndef); i++)
658 while ((ptr = strstrw(ptr, defs[i].name)) != nullptr)
661 ptr += strlen(defs[i].name);
667 len = strlen(buf) + nn*std::max(four, four+strlen(defs[i].def)-strlen(defs[i].name));
670 while ((ptr2 = strstrw(ptr, defs[i].name)) != nullptr)
672 strncat(name, ptr, (int)(ptr2-ptr));
673 strcat(name, defs[i].def);
674 ptr = ptr2 + strlen(defs[i].name);
686 char *cpp_cur_file(const gmx_cpp_t *handlep)
688 return (*handlep)->fn;
691 int cpp_cur_linenr(const gmx_cpp_t *handlep)
693 return (*handlep)->line_nr;
696 /* Close the file! Return integer status. */
697 int cpp_close_file(gmx_cpp_t *handlep)
699 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
703 return eCPP_INVALID_HANDLE;
707 return eCPP_FILE_NOT_OPEN;
710 if (nullptr != handle->cwd)
712 gmx_chdir(handle->cwd);
722 return eCPP_FILE_NOT_FOUND;
724 return eCPP_FILE_NOT_OPEN;
726 return eCPP_INTERRUPT;
731 handle->fp = nullptr;
733 if (nullptr != handle->fn)
736 handle->fn = nullptr;
738 if (nullptr != handle->line)
741 handle->line = nullptr;
743 if (nullptr != handle->ifdefs)
745 sfree(handle->ifdefs);
748 if (nullptr != handle->path)
752 if (nullptr != handle->cwd)
766 /* Return a string containing the error message coresponding to status
768 char *cpp_error(gmx_cpp_t *handlep, int status)
771 const char *ecpp[] = {
772 "OK", "File not found", "End of file", "Syntax error", "Interrupted",
773 "Invalid file handle",
774 "File not open", "Unknown error", "Error status out of range"
776 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
780 return (char *)ecpp[eCPP_INVALID_HANDLE];
783 if ((status < 0) || (status >= eCPP_NR))
788 sprintf(buf, "%s - File %s, line %d\nLast line read:\n'%s'",
790 (handle && handle->fn) ? handle->fn : "unknown",
791 (handle) ? handle->line_nr : -1,
792 handle->line ? handle->line : "");
794 return gmx_strdup(buf);