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.
51 #include <sys/types.h>
53 #include "gromacs/utility/cstringutil.h"
54 #include "gromacs/utility/dir_separator.h"
55 #include "gromacs/utility/fatalerror.h"
56 #include "gromacs/utility/futil.h"
57 #include "gromacs/utility/gmxassert.h"
58 #include "gromacs/utility/smalloc.h"
66 static t_define
*defs
= nullptr;
68 static char **incl
= nullptr;
70 /* enum used for handling ifdefs */
72 eifTRUE
, eifFALSE
, eifIGNORE
, eifNR
75 typedef struct gmx_cpp
{
84 struct gmx_cpp
*child
, *parent
;
87 static bool is_word_end(char c
)
89 return !((isalnum(c
) != 0) || c
== '_');
92 static const char *strstrw(const char *buf
, const char *word
)
96 while ((ptr
= strstr(buf
, word
)) != nullptr)
98 /* Check if we did not find part of a longer word */
100 is_word_end(ptr
[strlen(word
)]) &&
101 (((ptr
> buf
) && is_word_end(ptr
[-1])) || (ptr
== buf
)))
106 buf
= ptr
+ strlen(word
);
111 /* Finds a preprocessor directive, whose name (after the '#') is
112 * returned in *name, and the remainder of the line after leading
113 * whitespace is returned in *val (which can be nullptr). */
114 static bool find_directive(char *buf
, char **name
, char **val
)
116 /* Skip initial whitespace */
117 while (isspace(*buf
))
121 /* Check if this is a directive */
126 /* Skip the hash and any space after it */
128 while (isspace(*buf
))
132 /* Set the name pointer and find the next space */
134 while (*buf
!= 0 && !isspace(*buf
))
138 /* Set the end of the name here, and skip any space */
143 while (isspace(*buf
))
148 /* Check if anything is remaining */
149 *val
= (*buf
!= 0) ? buf
: nullptr;
153 static bool is_ifdeffed_out(gmx_cpp_t handle
)
155 return ((handle
->nifdef
> 0) && (handle
->ifdefs
[handle
->nifdef
-1] != eifTRUE
));
158 static void add_include(const char *include
)
162 if (include
== nullptr)
167 for (i
= 0; (i
< nincl
); i
++)
169 if (strcmp(incl
[i
], include
) == 0)
178 incl
[nincl
-1] = gmx_strdup(include
);
182 static void done_includes()
185 for (i
= 0; (i
< nincl
); i
++)
194 static void add_define(const char *name
, const char *value
)
198 for (i
= 0; (i
< ndef
); i
++)
200 if (strcmp(defs
[i
].name
, name
) == 0)
210 defs
[i
].name
= gmx_strdup(name
);
212 else if (defs
[i
].def
)
216 if (value
&& strlen(value
) > 0)
218 defs
[i
].def
= gmx_strdup(value
);
222 defs
[i
].def
= nullptr;
226 static void done_defines()
229 for (i
= 0; (i
< ndef
); i
++)
239 /* Open the file to be processed. The handle variable holds internal
240 info for the cpp emulator. Return integer status */
241 int cpp_open_file(const char *filenm
, gmx_cpp_t
*handle
, char **cppopts
)
248 /* First process options, they might be necessary for opening files
249 (especially include statements). */
255 if (strstr(cppopts
[i
], "-I") == cppopts
[i
])
257 add_include(cppopts
[i
]+2);
259 if (strstr(cppopts
[i
], "-D") == cppopts
[i
])
261 /* If the option contains a =, split it into name and value. */
262 ptr
= strchr(cppopts
[i
], '=');
265 buf
= gmx_strndup(cppopts
[i
] + 2, ptr
- cppopts
[i
] - 2);
266 add_define(buf
, ptr
+ 1);
271 add_define(cppopts
[i
] + 2, nullptr);
281 /* Find the file. First check whether it is in the current directory. */
282 if (gmx_fexist(filenm
))
284 cpp
->fn
= gmx_strdup(filenm
);
288 /* If not, check all the paths given with -I. */
289 for (i
= 0; i
< nincl
; ++i
)
291 snew(buf
, strlen(incl
[i
]) + strlen(filenm
) + 2);
292 sprintf(buf
, "%s/%s", incl
[i
], filenm
);
300 /* If still not found, check the Gromacs library search path. */
303 cpp
->fn
= low_gmxlibfn(filenm
, FALSE
, FALSE
);
308 gmx_fatal(FARGS
, "Topology include file \"%s\" not found", filenm
);
310 /* If the file name has a path component, we need to change to that
311 * directory. Note that we - just as C - always use UNIX path separators
312 * internally in include file names.
314 ptr
= strrchr(cpp
->fn
, '/');
315 ptr2
= strrchr(cpp
->fn
, DIR_SEPARATOR
);
317 if (ptr
== nullptr || (ptr2
!= nullptr && ptr2
> ptr
))
330 cpp
->fn
= gmx_strdup(ptr
+1);
331 snew(cpp
->cwd
, STRLEN
);
333 gmx_getcwd(cpp
->cwd
, STRLEN
);
334 gmx_chdir(cpp
->path
);
340 cpp
->ifdefs
= nullptr;
341 cpp
->child
= nullptr;
342 cpp
->parent
= nullptr;
343 if (cpp
->fp
== nullptr)
345 cpp
->fp
= fopen(cpp
->fn
, "r");
347 if (cpp
->fp
== nullptr)
359 /* Note that dval might be null, e.g. when handling a line like '#define */
361 process_directive(gmx_cpp_t
*handlep
, const char *dname
, const char *dval
)
363 gmx_cpp_t handle
= *handlep
;
364 int i
, i0
, len
, status
;
368 bool bIfdef
, bIfndef
;
370 /* #ifdef or ifndef statement */
371 bIfdef
= strcmp(dname
, "ifdef") == 0;
372 bIfndef
= strcmp(dname
, "ifndef") == 0;
373 if (bIfdef
|| bIfndef
)
375 GMX_RELEASE_ASSERT(dval
, "#ifdef/#ifndef requires an argument");
376 if ((handle
->nifdef
> 0) && (handle
->ifdefs
[handle
->nifdef
-1] != eifTRUE
))
379 srenew(handle
->ifdefs
, handle
->nifdef
);
380 handle
->ifdefs
[handle
->nifdef
-1] = eifIGNORE
;
384 // A bare '#ifdef' or '#ifndef' is invalid
389 snew(name
, strlen(dval
)+1);
390 sscanf(dval
, "%s", name
);
391 for (i
= 0; (i
< ndef
); i
++)
393 if (strcmp(defs
[i
].name
, name
) == 0)
399 srenew(handle
->ifdefs
, handle
->nifdef
);
400 if ((bIfdef
&& (i
< ndef
)) || (bIfndef
&& (i
== ndef
)))
402 handle
->ifdefs
[handle
->nifdef
-1] = eifTRUE
;
406 handle
->ifdefs
[handle
->nifdef
-1] = eifFALSE
;
413 /* #else statement */
414 if (strcmp(dname
, "else") == 0)
416 if (handle
->nifdef
<= 0)
420 if (handle
->ifdefs
[handle
->nifdef
-1] == eifTRUE
)
422 handle
->ifdefs
[handle
->nifdef
-1] = eifFALSE
;
424 else if (handle
->ifdefs
[handle
->nifdef
-1] == eifFALSE
)
426 handle
->ifdefs
[handle
->nifdef
-1] = eifTRUE
;
431 /* #endif statement */
432 if (strcmp(dname
, "endif") == 0)
434 if (handle
->nifdef
<= 0)
442 /* Check whether we're not ifdeffed out. The order of this statement
443 is important. It has to come after #ifdef, #else and #endif, but
444 anything else should be ignored. */
445 if (is_ifdeffed_out(handle
))
450 /* Check for include statements */
451 if (strcmp(dname
, "include") == 0)
453 GMX_RELEASE_ASSERT(dval
, "#include requires an argument");
456 // A bare '#include' is an invalid line
461 for (i1
= 0; (i1
< strlen(dval
)); i1
++)
463 if ((dval
[i1
] == '"') || (dval
[i1
] == '<') || (dval
[i1
] == '>'))
485 strncpy(inc_fn
, dval
+i0
, len
);
488 /* Open include file and store it as a child in the handle structure */
489 status
= cpp_open_file(inc_fn
, &(handle
->child
), nullptr);
491 if (status
!= eCPP_OK
)
493 handle
->child
= nullptr;
496 /* Make a linked list of open files and move on to the include file */
497 handle
->child
->parent
= handle
;
498 *handlep
= handle
->child
;
502 /* #define statement */
503 if (strcmp(dname
, "define") == 0)
505 // A bare '#define' is an invalid line
510 /* Split it into name and value. */
512 while ((*ptr
!= '\0') && !isspace(*ptr
))
516 name
= gmx_strndup(dval
, ptr
- dval
);
518 while ((*ptr
!= '\0') && isspace(*ptr
))
523 add_define(name
, ptr
);
528 /* #undef statement */
529 if (strcmp(dname
, "undef") == 0)
531 // A bare '#undef' is an invalid line
536 snew(name
, strlen(dval
)+1);
537 sscanf(dval
, "%s", name
);
538 for (i
= 0; (i
< ndef
); i
++)
540 if (strcmp(defs
[i
].name
, name
) == 0)
548 for (; (i
< ndef
-1); i
++)
550 defs
[i
].name
= defs
[i
+1].name
;
551 defs
[i
].def
= defs
[i
+1].def
;
558 /* If we haven't matched anything, this is an unknown directive */
562 /* Return one whole line from the file into buf which holds at most n
563 characters, for subsequent processing. Returns integer status. This
564 routine also does all the "intelligent" work like processing cpp
565 directives and so on. Note that often the routine is called
566 recursively and no cpp directives are printed. */
567 int cpp_read_line(gmx_cpp_t
*handlep
, int n
, char buf
[])
569 gmx_cpp_t handle
= *handlep
;
570 int i
, nn
, len
, status
;
571 const char *ptr
, *ptr2
;
578 return eCPP_INVALID_HANDLE
;
582 return eCPP_FILE_NOT_OPEN
;
585 bEOF
= (feof(handle
->fp
) != 0);
588 /* Read the actual line now. */
589 if (fgets2(buf
, n
-1, handle
->fp
) == nullptr)
591 /* Recheck EOF, since we could have been at the end before
592 * the fgets2 call, but we need to read past the end to know.
594 bEOF
= (feof(handle
->fp
) != 0);
597 /* Something strange happened, fgets returned NULL,
598 * but we are not at EOF.
607 if (handle
->parent
== nullptr)
611 cpp_close_file(handlep
);
612 *handlep
= handle
->parent
;
613 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
, static_cast<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
= *handlep
;
703 return eCPP_INVALID_HANDLE
;
707 return eCPP_FILE_NOT_OPEN
;
710 if (nullptr != handle
->cwd
)
712 gmx_chdir(handle
->cwd
);
715 handle
->fp
= nullptr;
717 if (nullptr != handle
->fn
)
720 handle
->fn
= nullptr;
722 if (nullptr != handle
->line
)
725 handle
->line
= nullptr;
727 if (nullptr != handle
->ifdefs
)
729 sfree(handle
->ifdefs
);
732 if (nullptr != handle
->path
)
736 if (nullptr != handle
->cwd
)
744 void cpp_done(gmx_cpp_t handle
)
749 int status
= cpp_close_file(&handle
);
750 if (status
!= eCPP_OK
)
752 gmx_fatal(FARGS
, "%s", cpp_error(&handle
, status
));
757 /* Return a string containing the error message coresponding to status
759 char *cpp_error(gmx_cpp_t
*handlep
, int status
)
762 const char *ecpp
[] = {
763 "OK", "File not found", "End of file", "Syntax error", "Interrupted",
764 "Invalid file handle",
765 "File not open", "Unknown error", "Error status out of range"
767 gmx_cpp_t handle
= *handlep
;
771 return const_cast<char *>(ecpp
[eCPP_INVALID_HANDLE
]);
774 if ((status
< 0) || (status
>= eCPP_NR
))
779 sprintf(buf
, "%s - File %s, line %d\nLast line read:\n'%s'",
781 (handle
&& handle
->fn
) ? handle
->fn
: "unknown",
782 (handle
) ? handle
->line_nr
: -1,
783 handle
->line
? handle
->line
: "");
785 return gmx_strdup(buf
);