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