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