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, 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.
41 #include <sys/types.h>
52 #include "gromacs/fileio/futil.h"
54 #include "gmx_fatal.h"
63 static t_define *defs = NULL;
65 static char **incl = 0;
67 /* enum used for handling ifdefs */
69 eifTRUE, eifFALSE, eifIGNORE, eifNR
72 typedef struct gmx_cpp {
81 struct gmx_cpp *child, *parent;
84 static gmx_bool is_word_end(char c)
86 return !(isalnum(c) || c == '_');
89 static const char *strstrw(const char *buf, const char *word)
93 while ((ptr = strstr(buf, word)) != NULL)
95 /* Check if we did not find part of a longer word */
97 is_word_end(ptr[strlen(word)]) &&
98 (((ptr > buf) && is_word_end(ptr[-1])) || (ptr == buf)))
103 buf = ptr + strlen(word);
108 static gmx_bool find_directive(char *buf, char **name, char **val)
110 /* Skip initial whitespace */
111 while (isspace(*buf))
115 /* Check if this is a directive */
120 /* Skip the hash and any space after it */
122 while (isspace(*buf))
126 /* Set the name pointer and find the next space */
128 while (*buf != 0 && !isspace(*buf))
132 /* Set the end of the name here, and skip any space */
137 while (isspace(*buf))
142 /* Check if anything is remaining */
143 *val = (*buf != 0) ? buf : NULL;
147 static gmx_bool is_ifdeffed_out(gmx_cpp_t handle)
149 return ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE));
152 static void add_include(const char *include)
161 for (i = 0; (i < nincl); i++)
163 if (strcmp(incl[i], include) == 0)
172 incl[nincl-1] = strdup(include);
176 static void done_includes()
179 for (i = 0; (i < nincl); i++)
188 static void add_define(const char *name, const char *value)
192 for (i = 0; (i < ndef); i++)
194 if (strcmp(defs[i].name, name) == 0)
204 defs[i].name = strdup(name);
206 else if (defs[i].def)
210 fprintf(debug, "Overriding define %s\n", name);
214 if (value && strlen(value) > 0)
216 defs[i].def = strdup(value);
224 static void done_defines()
227 for (i = 0; (i < ndef); i++)
237 /* Open the file to be processed. The handle variable holds internal
238 info for the cpp emulator. Return integer status */
239 int cpp_open_file(const char *filenm, gmx_cpp_t *handle, char **cppopts)
247 /* First process options, they might be necessary for opening files
248 (especially include statements). */
254 if (strstr(cppopts[i], "-I") == cppopts[i])
256 add_include(cppopts[i]+2);
258 if (strstr(cppopts[i], "-D") == cppopts[i])
260 /* If the option contains a =, split it into name and value. */
261 ptr = strchr(cppopts[i], '=');
264 buf = gmx_strndup(cppopts[i] + 2, ptr - cppopts[i] - 2);
265 add_define(buf, ptr + 1);
270 add_define(cppopts[i] + 2, NULL);
278 fprintf(debug, "GMXCPP: added %d command line arguments\n", i);
284 /* Find the file. First check whether it is in the current directory. */
285 if (gmx_fexist(filenm))
287 cpp->fn = strdup(filenm);
291 /* If not, check all the paths given with -I. */
292 for (i = 0; i < nincl; ++i)
294 snew(buf, strlen(incl[i]) + strlen(filenm) + 2);
295 sprintf(buf, "%s/%s", incl[i], filenm);
303 /* If still not found, check the Gromacs library search path. */
306 cpp->fn = low_gmxlibfn(filenm, FALSE, FALSE);
311 gmx_fatal(FARGS, "Topology include file \"%s\" not found", filenm);
315 fprintf(debug, "GMXCPP: cpp file open %s\n", cpp->fn);
317 /* If the file name has a path component, we need to change to that
318 * directory. Note that we - just as C - always use UNIX path separators
319 * internally in include file names.
321 ptr = strrchr(cpp->fn, '/');
322 ptr2 = strrchr(cpp->fn, DIR_SEPARATOR);
324 if (ptr == NULL || (ptr2 != NULL && ptr2 > ptr))
337 cpp->fn = strdup(ptr+1);
338 snew(cpp->cwd, STRLEN);
340 gmx_getcwd(cpp->cwd, STRLEN);
343 fprintf(debug, "GMXCPP: cwd %s\n", cpp->cwd);
345 gmx_chdir(cpp->path);
349 fprintf(debug, "GMXCPP: chdir to %s\n", cpp->path);
363 fprintf(debug, "GMXCPP: opening file %s\n", cpp->fn);
365 cpp->fp = fopen(cpp->fn, "r");
380 process_directive(gmx_cpp_t *handlep, const char *dname, const char *dval)
382 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
383 int i, i0, len, status;
389 /* #ifdef or ifndef statement */
390 bIfdef = (strcmp(dname, "ifdef") == 0);
391 bIfndef = (strcmp(dname, "ifndef") == 0);
392 if (bIfdef || bIfndef)
394 if ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE))
397 srenew(handle->ifdefs, handle->nifdef);
398 handle->ifdefs[handle->nifdef-1] = eifIGNORE;
402 snew(name, strlen(dval)+1);
403 sscanf(dval, "%s", name);
404 for (i = 0; (i < ndef); i++)
406 if (strcmp(defs[i].name, name) == 0)
412 srenew(handle->ifdefs, handle->nifdef);
413 if ((bIfdef && (i < ndef)) || (bIfndef && (i == ndef)))
415 handle->ifdefs[handle->nifdef-1] = eifTRUE;
419 handle->ifdefs[handle->nifdef-1] = eifFALSE;
426 /* #else statement */
427 if (strcmp(dname, "else") == 0)
429 if (handle->nifdef <= 0)
433 if (handle->ifdefs[handle->nifdef-1] == eifTRUE)
435 handle->ifdefs[handle->nifdef-1] = eifFALSE;
437 else if (handle->ifdefs[handle->nifdef-1] == eifFALSE)
439 handle->ifdefs[handle->nifdef-1] = eifTRUE;
444 /* #endif statement */
445 if (strcmp(dname, "endif") == 0)
447 if (handle->nifdef <= 0)
455 /* Check whether we're not ifdeffed out. The order of this statement
456 is important. It has to come after #ifdef, #else and #endif, but
457 anything else should be ignored. */
458 if (is_ifdeffed_out(handle))
463 /* Check for include statements */
464 if (strcmp(dname, "include") == 0)
468 for (i1 = 0; (i1 < strlen(dval)); i1++)
470 if ((dval[i1] == '"') || (dval[i1] == '<') || (dval[i1] == '>'))
492 strncpy(inc_fn, dval+i0, len);
497 fprintf(debug, "Going to open include file '%s' i0 = %d, strlen = %d\n",
500 /* Open include file and store it as a child in the handle structure */
501 status = cpp_open_file(inc_fn, &(handle->child), NULL);
503 if (status != eCPP_OK)
505 handle->child = NULL;
508 /* Make a linked list of open files and move on to the include file */
509 handle->child->parent = handle;
510 *handlep = handle->child;
515 /* #define statement */
516 if (strcmp(dname, "define") == 0)
518 /* Split it into name and value. */
520 while ((*ptr != '\0') && !isspace(*ptr))
524 name = gmx_strndup(dval, ptr - dval);
526 while ((*ptr != '\0') && isspace(*ptr))
531 add_define(name, ptr);
536 /* #undef statement */
537 if (strcmp(dname, "undef") == 0)
539 snew(name, strlen(dval)+1);
540 sscanf(dval, "%s", name);
541 for (i = 0; (i < ndef); i++)
543 if (strcmp(defs[i].name, name) == 0)
551 for (; (i < ndef-1); i++)
553 defs[i].name = defs[i+1].name;
554 defs[i].def = defs[i+1].def;
561 /* If we haven't matched anything, this is an unknown directive */
565 /* Return one whole line from the file into buf which holds at most n
566 characters, for subsequent processing. Returns integer status. This
567 routine also does all the "intelligent" work like processing cpp
568 directives and so on. Note that often the routine is called
569 recursively and no cpp directives are printed. */
570 int cpp_read_line(gmx_cpp_t *handlep, int n, char buf[])
572 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
573 int i, nn, len, status;
574 const char *ptr, *ptr2;
581 return eCPP_INVALID_HANDLE;
585 return eCPP_FILE_NOT_OPEN;
588 bEOF = feof(handle->fp);
591 /* Read the actual line now. */
592 if (fgets2(buf, n-1, handle->fp) == NULL)
594 /* Recheck EOF, since we could have been at the end before
595 * the fgets2 call, but we need to read past the end to know.
597 bEOF = feof(handle->fp);
600 /* Something strange happened, fgets returned NULL,
601 * but we are not at EOF.
610 if (handle->parent == NULL)
614 cpp_close_file(handlep);
615 *handlep = handle->parent;
616 handle->child = NULL;
617 return cpp_read_line(handlep, n, buf);
621 if (n > handle->line_len)
623 handle->line_len = n;
624 srenew(handle->line, n);
626 strcpy(handle->line, buf);
629 /* Now we've read a line! */
632 fprintf(debug, "%s : %4d : %s\n", handle->fn, handle->line_nr, buf);
635 /* Process directives if this line contains one */
636 if (find_directive(buf, &dname, &dval))
638 status = process_directive(handlep, dname, dval);
639 if (status != eCPP_OK)
643 /* Don't print lines with directives, go on to the next */
644 return cpp_read_line(handlep, n, buf);
647 /* Check whether we're not ifdeffed out. The order of this statement
648 is important. It has to come after #ifdef, #else and #endif, but
649 anything else should be ignored. */
650 if (is_ifdeffed_out(handle))
652 return cpp_read_line(handlep, n, buf);
655 /* Check whether we have any defines that need to be replaced. Note
656 that we have to use a best fit algorithm, rather than first come
657 first go. We do this by sorting the defines on length first, and
658 then on alphabetical order. */
659 for (i = 0; (i < ndef); i++)
665 while ((ptr = strstrw(ptr, defs[i].name)) != NULL)
668 ptr += strlen(defs[i].name);
672 len = strlen(buf) + nn*max(4, 4+strlen(defs[i].def)-strlen(defs[i].name));
675 while ((ptr2 = strstrw(ptr, defs[i].name)) != NULL)
677 strncat(name, ptr, (int)(ptr2-ptr));
678 strcat(name, defs[i].def);
679 ptr = ptr2 + strlen(defs[i].name);
691 char *cpp_cur_file(const gmx_cpp_t *handlep)
693 return (*handlep)->fn;
696 int cpp_cur_linenr(const gmx_cpp_t *handlep)
698 return (*handlep)->line_nr;
701 /* Close the file! Return integer status. */
702 int cpp_close_file(gmx_cpp_t *handlep)
705 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
709 return eCPP_INVALID_HANDLE;
713 return eCPP_FILE_NOT_OPEN;
717 fprintf(debug, "GMXCPP: closing file %s\n", handle->fn);
720 if (NULL != handle->cwd)
724 fprintf(debug, "GMXCPP: chdir to %s\n", handle->cwd);
726 gmx_chdir(handle->cwd);
736 return eCPP_FILE_NOT_FOUND;
738 return eCPP_FILE_NOT_OPEN;
740 return eCPP_INTERRUPT;
744 fprintf(debug, "Strange stuff closing file, errno = %d", errno);
751 if (NULL != handle->fn)
756 if (NULL != handle->line)
761 if (NULL != handle->ifdefs)
763 sfree(handle->ifdefs);
766 if (NULL != handle->path)
770 if (NULL != handle->cwd)
784 /* Return a string containing the error message coresponding to status
786 char *cpp_error(gmx_cpp_t *handlep, int status)
789 const char *ecpp[] = {
790 "OK", "File not found", "End of file", "Syntax error", "Interrupted",
791 "Invalid file handle",
792 "File not open", "Unknown error", "Error status out of range"
794 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
798 return (char *)ecpp[eCPP_INVALID_HANDLE];
801 if ((status < 0) || (status >= eCPP_NR))
806 sprintf(buf, "%s - File %s, line %d\nLast line read:\n'%s'",
808 (handle && handle->fn) ? handle->fn : "unknown",
809 (handle) ? handle->line_nr : -1,
810 handle->line ? handle->line : "");