change gmx_bool to bool in gmxpreprocess
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / gmxcpp.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 #include "gmxpre.h"
38
39 #include "gmxcpp.h"
40
41 #include <ctype.h>
42 #include <errno.h>
43 #include <limits.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #include <cmath>
49
50 #include <algorithm>
51
52 #include <sys/types.h>
53
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"
60
61 typedef struct {
62     char *name;
63     char *def;
64 } t_define;
65
66 static int        ndef   = 0;
67 static t_define  *defs   = nullptr;
68 static int        nincl  = 0;
69 static char     **incl   = nullptr;
70
71 /* enum used for handling ifdefs */
72 enum {
73     eifTRUE, eifFALSE, eifIGNORE, eifNR
74 };
75
76 typedef struct gmx_cpp {
77     FILE             *fp;
78     char             *path, *cwd;
79     char             *fn;
80     int               line_len;
81     char             *line;
82     int               line_nr;
83     int               nifdef;
84     int              *ifdefs;
85     struct   gmx_cpp *child, *parent;
86 } gmx_cpp;
87
88 static bool is_word_end(char c)
89 {
90     return !(isalnum(c) || c == '_');
91 }
92
93 static const char *strstrw(const char *buf, const char *word)
94 {
95     const char *ptr;
96
97     while ((ptr = strstr(buf, word)) != nullptr)
98     {
99         /* Check if we did not find part of a longer word */
100         if (ptr &&
101             is_word_end(ptr[strlen(word)]) &&
102             (((ptr > buf) && is_word_end(ptr[-1])) || (ptr == buf)))
103         {
104             return ptr;
105         }
106
107         buf = ptr + strlen(word);
108     }
109     return nullptr;
110 }
111
112 static bool find_directive(char *buf, char **name, char **val)
113 {
114     /* Skip initial whitespace */
115     while (isspace(*buf))
116     {
117         ++buf;
118     }
119     /* Check if this is a directive */
120     if (*buf != '#')
121     {
122         return FALSE;
123     }
124     /* Skip the hash and any space after it */
125     ++buf;
126     while (isspace(*buf))
127     {
128         ++buf;
129     }
130     /* Set the name pointer and find the next space */
131     *name = buf;
132     while (*buf != 0 && !isspace(*buf))
133     {
134         ++buf;
135     }
136     /* Set the end of the name here, and skip any space */
137     if (*buf != 0)
138     {
139         *buf = 0;
140         ++buf;
141         while (isspace(*buf))
142         {
143             ++buf;
144         }
145     }
146     /* Check if anything is remaining */
147     *val = (*buf != 0) ? buf : nullptr;
148     return TRUE;
149 }
150
151 static bool is_ifdeffed_out(gmx_cpp_t handle)
152 {
153     return ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE));
154 }
155
156 static void add_include(const char *include)
157 {
158     int i;
159
160     if (include == nullptr)
161     {
162         return;
163     }
164
165     for (i = 0; (i < nincl); i++)
166     {
167         if (strcmp(incl[i], include) == 0)
168         {
169             break;
170         }
171     }
172     if (i == nincl)
173     {
174         nincl++;
175         srenew(incl, nincl);
176         incl[nincl-1] = gmx_strdup(include);
177     }
178 }
179
180 static void done_includes()
181 {
182     int i;
183     for (i = 0; (i < nincl); i++)
184     {
185         sfree(incl[i]);
186     }
187     sfree(incl);
188     incl  = nullptr;
189     nincl = 0;
190 }
191
192 static void add_define(const char *name, const char *value)
193 {
194     int  i;
195
196     for (i = 0; (i < ndef); i++)
197     {
198         if (strcmp(defs[i].name, name) == 0)
199         {
200             break;
201         }
202     }
203     if (i == ndef)
204     {
205         ndef++;
206         srenew(defs, ndef);
207         i            = ndef - 1;
208         defs[i].name = gmx_strdup(name);
209     }
210     else if (defs[i].def)
211     {
212         if (debug)
213         {
214             fprintf(debug, "Overriding define %s\n", name);
215         }
216         sfree(defs[i].def);
217     }
218     if (value && strlen(value) > 0)
219     {
220         defs[i].def  = gmx_strdup(value);
221     }
222     else
223     {
224         defs[i].def  = nullptr;
225     }
226 }
227
228 static void done_defines()
229 {
230     int i;
231     for (i = 0; (i < ndef); i++)
232     {
233         sfree(defs[i].name);
234         sfree(defs[i].def);
235     }
236     sfree(defs);
237     defs = nullptr;
238     ndef = 0;
239 }
240
241 /* Open the file to be processed. The handle variable holds internal
242    info for the cpp emulator. Return integer status */
243 int cpp_open_file(const char *filenm, gmx_cpp_t *handle, char **cppopts)
244 {
245     gmx_cpp_t    cpp;
246     char        *buf;
247     char        *ptr, *ptr2;
248     int          i;
249
250     /* First process options, they might be necessary for opening files
251        (especially include statements). */
252     i  = 0;
253     if (cppopts)
254     {
255         while (cppopts[i])
256         {
257             if (strstr(cppopts[i], "-I") == cppopts[i])
258             {
259                 add_include(cppopts[i]+2);
260             }
261             if (strstr(cppopts[i], "-D") == cppopts[i])
262             {
263                 /* If the option contains a =, split it into name and value. */
264                 ptr = strchr(cppopts[i], '=');
265                 if (ptr)
266                 {
267                     buf = gmx_strndup(cppopts[i] + 2, ptr - cppopts[i] - 2);
268                     add_define(buf, ptr + 1);
269                     sfree(buf);
270                 }
271                 else
272                 {
273                     add_define(cppopts[i] + 2, nullptr);
274                 }
275             }
276             i++;
277         }
278     }
279     if (debug)
280     {
281         fprintf(debug, "GMXCPP: added %d command line arguments\n", i);
282     }
283
284     snew(cpp, 1);
285     *handle      = cpp;
286     cpp->fn      = nullptr;
287     /* Find the file. First check whether it is in the current directory. */
288     if (gmx_fexist(filenm))
289     {
290         cpp->fn = gmx_strdup(filenm);
291     }
292     else
293     {
294         /* If not, check all the paths given with -I. */
295         for (i = 0; i < nincl; ++i)
296         {
297             snew(buf, strlen(incl[i]) + strlen(filenm) + 2);
298             sprintf(buf, "%s/%s", incl[i], filenm);
299             if (gmx_fexist(buf))
300             {
301                 cpp->fn = buf;
302                 break;
303             }
304             sfree(buf);
305         }
306         /* If still not found, check the Gromacs library search path. */
307         if (!cpp->fn)
308         {
309             cpp->fn = low_gmxlibfn(filenm, FALSE, FALSE);
310         }
311     }
312     if (!cpp->fn)
313     {
314         gmx_fatal(FARGS, "Topology include file \"%s\" not found", filenm);
315     }
316     if (nullptr != debug)
317     {
318         fprintf(debug, "GMXCPP: cpp file open %s\n", cpp->fn);
319     }
320     /* If the file name has a path component, we need to change to that
321      * directory. Note that we - just as C - always use UNIX path separators
322      * internally in include file names.
323      */
324     ptr  = strrchr(cpp->fn, '/');
325     ptr2 = strrchr(cpp->fn, DIR_SEPARATOR);
326
327     if (ptr == nullptr || (ptr2 != nullptr && ptr2 > ptr))
328     {
329         ptr = ptr2;
330     }
331     if (ptr == nullptr)
332     {
333         cpp->path = nullptr;
334         cpp->cwd  = nullptr;
335     }
336     else
337     {
338         cpp->path = cpp->fn;
339         *ptr      = '\0';
340         cpp->fn   = gmx_strdup(ptr+1);
341         snew(cpp->cwd, STRLEN);
342
343         gmx_getcwd(cpp->cwd, STRLEN);
344         if (nullptr != debug)
345         {
346             fprintf(debug, "GMXCPP: cwd %s\n", cpp->cwd);
347         }
348         gmx_chdir(cpp->path);
349
350         if (nullptr != debug)
351         {
352             fprintf(debug, "GMXCPP: chdir to %s\n", cpp->path);
353         }
354     }
355     cpp->line_len = 0;
356     cpp->line     = nullptr;
357     cpp->line_nr  = 0;
358     cpp->nifdef   = 0;
359     cpp->ifdefs   = nullptr;
360     cpp->child    = nullptr;
361     cpp->parent   = nullptr;
362     if (cpp->fp == nullptr)
363     {
364         if (nullptr != debug)
365         {
366             fprintf(debug, "GMXCPP: opening file %s\n", cpp->fn);
367         }
368         cpp->fp = fopen(cpp->fn, "r");
369     }
370     if (cpp->fp == nullptr)
371     {
372         switch (errno)
373         {
374             case EINVAL:
375             default:
376                 return eCPP_UNKNOWN;
377         }
378     }
379     return eCPP_OK;
380 }
381
382 static int
383 process_directive(gmx_cpp_t *handlep, const char *dname, const char *dval)
384 {
385     gmx_cpp_t    handle = (gmx_cpp_t)*handlep;
386     int          i, i0, len, status;
387     unsigned int i1;
388     char        *inc_fn, *name;
389     const char  *ptr;
390     int          bIfdef, bIfndef;
391
392     /* #ifdef or ifndef statement */
393     bIfdef  = (strcmp(dname, "ifdef") == 0);
394     bIfndef = (strcmp(dname, "ifndef") == 0);
395     if (bIfdef || bIfndef)
396     {
397         GMX_RELEASE_ASSERT(dval, "#ifdef/#ifndef requires an argument");
398         if ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE))
399         {
400             handle->nifdef++;
401             srenew(handle->ifdefs, handle->nifdef);
402             handle->ifdefs[handle->nifdef-1] = eifIGNORE;
403         }
404         else
405         {
406             snew(name, strlen(dval)+1);
407             sscanf(dval, "%s", name);
408             for (i = 0; (i < ndef); i++)
409             {
410                 if (strcmp(defs[i].name, name) == 0)
411                 {
412                     break;
413                 }
414             }
415             handle->nifdef++;
416             srenew(handle->ifdefs, handle->nifdef);
417             if ((bIfdef && (i < ndef)) || (bIfndef && (i == ndef)))
418             {
419                 handle->ifdefs[handle->nifdef-1] = eifTRUE;
420             }
421             else
422             {
423                 handle->ifdefs[handle->nifdef-1] = eifFALSE;
424             }
425             sfree(name);
426         }
427         return eCPP_OK;
428     }
429
430     /* #else statement */
431     if (strcmp(dname, "else") == 0)
432     {
433         if (handle->nifdef <= 0)
434         {
435             return eCPP_SYNTAX;
436         }
437         if (handle->ifdefs[handle->nifdef-1] == eifTRUE)
438         {
439             handle->ifdefs[handle->nifdef-1] = eifFALSE;
440         }
441         else if (handle->ifdefs[handle->nifdef-1] == eifFALSE)
442         {
443             handle->ifdefs[handle->nifdef-1] = eifTRUE;
444         }
445         return eCPP_OK;
446     }
447
448     /* #endif statement */
449     if (strcmp(dname, "endif") == 0)
450     {
451         if (handle->nifdef <= 0)
452         {
453             return eCPP_SYNTAX;
454         }
455         handle->nifdef--;
456         return eCPP_OK;
457     }
458
459     /* Check whether we're not ifdeffed out. The order of this statement
460        is important. It has to come after #ifdef, #else and #endif, but
461        anything else should be ignored. */
462     if (is_ifdeffed_out(handle))
463     {
464         return eCPP_OK;
465     }
466
467     /* Check for include statements */
468     if (strcmp(dname, "include") == 0)
469     {
470         GMX_RELEASE_ASSERT(dval, "#include requires an argument");
471         len = -1;
472         i0  = 0;
473         for (i1 = 0; (i1 < strlen(dval)); i1++)
474         {
475             if ((dval[i1] == '"') || (dval[i1] == '<') || (dval[i1] == '>'))
476             {
477                 if (len == -1)
478                 {
479                     i0  = i1+1;
480                     len = 0;
481                 }
482                 else
483                 {
484                     break;
485                 }
486             }
487             else if (len >= 0)
488             {
489                 len++;
490             }
491         }
492         if (len == -1)
493         {
494             return eCPP_SYNTAX;
495         }
496         snew(inc_fn, len+1);
497         strncpy(inc_fn, dval+i0, len);
498         inc_fn[len] = '\0';
499
500         if (debug)
501         {
502             fprintf(debug, "Going to open include file '%s' i0 = %d, strlen = %d\n",
503                     inc_fn, i0, len);
504         }
505         /* Open include file and store it as a child in the handle structure */
506         status = cpp_open_file(inc_fn, &(handle->child), nullptr);
507         sfree(inc_fn);
508         if (status != eCPP_OK)
509         {
510             handle->child = nullptr;
511             return status;
512         }
513         /* Make a linked list of open files and move on to the include file */
514         handle->child->parent = handle;
515         *handlep              = handle->child;
516         return eCPP_OK;
517     }
518
519     /* #define statement */
520     if (strcmp(dname, "define") == 0)
521     {
522         GMX_RELEASE_ASSERT(dval, "#define requires an argument");
523         /* Split it into name and value. */
524         ptr = dval;
525         while ((*ptr != '\0') && !isspace(*ptr))
526         {
527             ptr++;
528         }
529         name = gmx_strndup(dval, ptr - dval);
530
531         while ((*ptr != '\0') && isspace(*ptr))
532         {
533             ptr++;
534         }
535
536         add_define(name, ptr);
537         sfree(name);
538         return eCPP_OK;
539     }
540
541     /* #undef statement */
542     if (strcmp(dname, "undef") == 0)
543     {
544         GMX_RELEASE_ASSERT(dval, "#undef requires an argument");
545         snew(name, strlen(dval)+1);
546         sscanf(dval, "%s", name);
547         for (i = 0; (i < ndef); i++)
548         {
549             if (strcmp(defs[i].name, name) == 0)
550             {
551                 sfree(defs[i].name);
552                 sfree(defs[i].def);
553                 break;
554             }
555         }
556         sfree(name);
557         for (; (i < ndef-1); i++)
558         {
559             defs[i].name = defs[i+1].name;
560             defs[i].def  = defs[i+1].def;
561         }
562         ndef--;
563
564         return eCPP_OK;
565     }
566
567     /* If we haven't matched anything, this is an unknown directive */
568     return eCPP_SYNTAX;
569 }
570
571 /* Return one whole line from the file into buf which holds at most n
572    characters, for subsequent processing. Returns integer status. This
573    routine also does all the "intelligent" work like processing cpp
574    directives and so on. Note that often the routine is called
575    recursively and no cpp directives are printed. */
576 int cpp_read_line(gmx_cpp_t *handlep, int n, char buf[])
577 {
578     gmx_cpp_t   handle = (gmx_cpp_t)*handlep;
579     int         i, nn, len, status;
580     const char *ptr, *ptr2;
581     char       *name;
582     char       *dname, *dval;
583     bool        bEOF;
584
585     if (!handle)
586     {
587         return eCPP_INVALID_HANDLE;
588     }
589     if (!handle->fp)
590     {
591         return eCPP_FILE_NOT_OPEN;
592     }
593
594     bEOF = feof(handle->fp);
595     if (!bEOF)
596     {
597         /* Read the actual line now. */
598         if (fgets2(buf, n-1, handle->fp) == nullptr)
599         {
600             /* Recheck EOF, since we could have been at the end before
601              * the fgets2 call, but we need to read past the end to know.
602              */
603             bEOF = feof(handle->fp);
604             if (!bEOF)
605             {
606                 /* Something strange happened, fgets returned NULL,
607                  * but we are not at EOF.
608                  */
609                 return eCPP_UNKNOWN;
610             }
611         }
612     }
613
614     if (bEOF)
615     {
616         if (handle->parent == nullptr)
617         {
618             return eCPP_EOF;
619         }
620         cpp_close_file(handlep);
621         *handlep      = handle->parent;
622         handle->child = nullptr;
623         return cpp_read_line(handlep, n, buf);
624     }
625     else
626     {
627         if (n > handle->line_len)
628         {
629             handle->line_len = n;
630             srenew(handle->line, n);
631         }
632         strcpy(handle->line, buf);
633         handle->line_nr++;
634     }
635     /* Now we've read a line! */
636     if (debug)
637     {
638         fprintf(debug, "%s : %4d : %s\n", handle->fn, handle->line_nr, buf);
639     }
640
641     /* Process directives if this line contains one */
642     if (find_directive(buf, &dname, &dval))
643     {
644         status = process_directive(handlep, dname, dval);
645         if (status != eCPP_OK)
646         {
647             return status;
648         }
649         /* Don't print lines with directives, go on to the next */
650         return cpp_read_line(handlep, n, buf);
651     }
652
653     /* Check whether we're not ifdeffed out. The order of this statement
654        is important. It has to come after #ifdef, #else and #endif, but
655        anything else should be ignored. */
656     if (is_ifdeffed_out(handle))
657     {
658         return cpp_read_line(handlep, n, buf);
659     }
660
661     /* Check whether we have any defines that need to be replaced. Note
662        that we have to use a best fit algorithm, rather than first come
663        first go. We do this by sorting the defines on length first, and
664        then on alphabetical order. */
665     for (i = 0; (i < ndef); i++)
666     {
667         if (defs[i].def)
668         {
669             nn  = 0;
670             ptr = buf;
671             while ((ptr = strstrw(ptr, defs[i].name)) != nullptr)
672             {
673                 nn++;
674                 ptr += strlen(defs[i].name);
675             }
676             if (nn > 0)
677             {
678                 size_t four = 4;
679
680                 len = strlen(buf) + nn*std::max(four, four+strlen(defs[i].def)-strlen(defs[i].name));
681                 snew(name, len);
682                 ptr = buf;
683                 while ((ptr2 = strstrw(ptr, defs[i].name)) != nullptr)
684                 {
685                     strncat(name, ptr, (int)(ptr2-ptr));
686                     strcat(name, defs[i].def);
687                     ptr = ptr2 + strlen(defs[i].name);
688                 }
689                 strcat(name, ptr);
690                 strcpy(buf, name);
691                 sfree(name);
692             }
693         }
694     }
695
696     return eCPP_OK;
697 }
698
699 char *cpp_cur_file(const gmx_cpp_t *handlep)
700 {
701     return (*handlep)->fn;
702 }
703
704 int cpp_cur_linenr(const gmx_cpp_t *handlep)
705 {
706     return (*handlep)->line_nr;
707 }
708
709 /* Close the file! Return integer status. */
710 int cpp_close_file(gmx_cpp_t *handlep)
711 {
712     gmx_cpp_t handle = (gmx_cpp_t)*handlep;
713
714     if (!handle)
715     {
716         return eCPP_INVALID_HANDLE;
717     }
718     if (!handle->fp)
719     {
720         return eCPP_FILE_NOT_OPEN;
721     }
722     if (debug)
723     {
724         fprintf(debug, "GMXCPP: closing file %s\n", handle->fn);
725     }
726     fclose(handle->fp);
727     if (nullptr != handle->cwd)
728     {
729         if (nullptr != debug)
730         {
731             fprintf(debug, "GMXCPP: chdir to %s\n", handle->cwd);
732         }
733         gmx_chdir(handle->cwd);
734     }
735
736     if (0)
737     {
738         switch (errno)
739         {
740             case 0:
741                 break;
742             case ENOENT:
743                 return eCPP_FILE_NOT_FOUND;
744             case EBADF:
745                 return eCPP_FILE_NOT_OPEN;
746             case EINTR:
747                 return eCPP_INTERRUPT;
748             default:
749                 if (debug)
750                 {
751                     fprintf(debug, "Strange stuff closing file, errno = %d", errno);
752                 }
753                 return eCPP_UNKNOWN;
754         }
755     }
756     handle->fp      = nullptr;
757     handle->line_nr = 0;
758     if (nullptr != handle->fn)
759     {
760         sfree(handle->fn);
761         handle->fn = nullptr;
762     }
763     if (nullptr != handle->line)
764     {
765         sfree(handle->line);
766         handle->line = nullptr;
767     }
768     if (nullptr != handle->ifdefs)
769     {
770         sfree(handle->ifdefs);
771     }
772     handle->nifdef = 0;
773     if (nullptr != handle->path)
774     {
775         sfree(handle->path);
776     }
777     if (nullptr != handle->cwd)
778     {
779         sfree(handle->cwd);
780     }
781
782     return eCPP_OK;
783 }
784
785 void cpp_done()
786 {
787     done_includes();
788     done_defines();
789 }
790
791 /* Return a string containing the error message coresponding to status
792    variable */
793 char *cpp_error(gmx_cpp_t *handlep, int status)
794 {
795     char        buf[256];
796     const char *ecpp[] = {
797         "OK", "File not found", "End of file", "Syntax error", "Interrupted",
798         "Invalid file handle",
799         "File not open", "Unknown error", "Error status out of range"
800     };
801     gmx_cpp_t   handle = (gmx_cpp_t)*handlep;
802
803     if (!handle)
804     {
805         return (char *)ecpp[eCPP_INVALID_HANDLE];
806     }
807
808     if ((status < 0) || (status >= eCPP_NR))
809     {
810         status = eCPP_NR;
811     }
812
813     sprintf(buf, "%s - File %s, line %d\nLast line read:\n'%s'",
814             ecpp[status],
815             (handle && handle->fn) ? handle->fn : "unknown",
816             (handle) ? handle->line_nr : -1,
817             handle->line ? handle->line : "");
818
819     return gmx_strdup(buf);
820 }