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