Move remaining C files in utility to C++
[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, 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/legacyheaders/macros.h"
55 #include "gromacs/utility/cstringutil.h"
56 #include "gromacs/utility/dir_separator.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/futil.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   = NULL;
68 static int        nincl  = 0;
69 static char     **incl   = 0;
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 gmx_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)) != NULL)
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 NULL;
110 }
111
112 static gmx_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 : NULL;
148     return TRUE;
149 }
150
151 static gmx_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 == NULL)
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  = NULL;
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  = NULL;
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 = NULL;
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, NULL);
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      = NULL;
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 (NULL != 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 == NULL || (ptr2 != NULL && ptr2 > ptr))
328     {
329         ptr = ptr2;
330     }
331     if (ptr == NULL)
332     {
333         cpp->path = NULL;
334         cpp->cwd  = NULL;
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 (NULL != debug)
345         {
346             fprintf(debug, "GMXCPP: cwd %s\n", cpp->cwd);
347         }
348         gmx_chdir(cpp->path);
349
350         if (NULL != debug)
351         {
352             fprintf(debug, "GMXCPP: chdir to %s\n", cpp->path);
353         }
354     }
355     cpp->line_len = 0;
356     cpp->line     = NULL;
357     cpp->line_nr  = 0;
358     cpp->nifdef   = 0;
359     cpp->ifdefs   = NULL;
360     cpp->child    = NULL;
361     cpp->parent   = NULL;
362     if (cpp->fp == NULL)
363     {
364         if (NULL != 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 == NULL)
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         if ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE))
398         {
399             handle->nifdef++;
400             srenew(handle->ifdefs, handle->nifdef);
401             handle->ifdefs[handle->nifdef-1] = eifIGNORE;
402         }
403         else
404         {
405             snew(name, strlen(dval)+1);
406             sscanf(dval, "%s", name);
407             for (i = 0; (i < ndef); i++)
408             {
409                 if (strcmp(defs[i].name, name) == 0)
410                 {
411                     break;
412                 }
413             }
414             handle->nifdef++;
415             srenew(handle->ifdefs, handle->nifdef);
416             if ((bIfdef && (i < ndef)) || (bIfndef && (i == ndef)))
417             {
418                 handle->ifdefs[handle->nifdef-1] = eifTRUE;
419             }
420             else
421             {
422                 handle->ifdefs[handle->nifdef-1] = eifFALSE;
423             }
424             sfree(name);
425         }
426         return eCPP_OK;
427     }
428
429     /* #else statement */
430     if (strcmp(dname, "else") == 0)
431     {
432         if (handle->nifdef <= 0)
433         {
434             return eCPP_SYNTAX;
435         }
436         if (handle->ifdefs[handle->nifdef-1] == eifTRUE)
437         {
438             handle->ifdefs[handle->nifdef-1] = eifFALSE;
439         }
440         else if (handle->ifdefs[handle->nifdef-1] == eifFALSE)
441         {
442             handle->ifdefs[handle->nifdef-1] = eifTRUE;
443         }
444         return eCPP_OK;
445     }
446
447     /* #endif statement */
448     if (strcmp(dname, "endif") == 0)
449     {
450         if (handle->nifdef <= 0)
451         {
452             return eCPP_SYNTAX;
453         }
454         handle->nifdef--;
455         return eCPP_OK;
456     }
457
458     /* Check whether we're not ifdeffed out. The order of this statement
459        is important. It has to come after #ifdef, #else and #endif, but
460        anything else should be ignored. */
461     if (is_ifdeffed_out(handle))
462     {
463         return eCPP_OK;
464     }
465
466     /* Check for include statements */
467     if (strcmp(dname, "include") == 0)
468     {
469         len = -1;
470         i0  = 0;
471         for (i1 = 0; (i1 < strlen(dval)); i1++)
472         {
473             if ((dval[i1] == '"') || (dval[i1] == '<') || (dval[i1] == '>'))
474             {
475                 if (len == -1)
476                 {
477                     i0  = i1+1;
478                     len = 0;
479                 }
480                 else
481                 {
482                     break;
483                 }
484             }
485             else if (len >= 0)
486             {
487                 len++;
488             }
489         }
490         if (len == -1)
491         {
492             return eCPP_SYNTAX;
493         }
494         snew(inc_fn, len+1);
495         strncpy(inc_fn, dval+i0, len);
496         inc_fn[len] = '\0';
497
498         if (debug)
499         {
500             fprintf(debug, "Going to open include file '%s' i0 = %d, strlen = %d\n",
501                     inc_fn, i0, len);
502         }
503         /* Open include file and store it as a child in the handle structure */
504         status = cpp_open_file(inc_fn, &(handle->child), NULL);
505         sfree(inc_fn);
506         if (status != eCPP_OK)
507         {
508             handle->child = NULL;
509             return status;
510         }
511         /* Make a linked list of open files and move on to the include file */
512         handle->child->parent = handle;
513         *handlep              = handle->child;
514         return eCPP_OK;
515     }
516
517     /* #define statement */
518     if (strcmp(dname, "define") == 0)
519     {
520         /* Split it into name and value. */
521         ptr = dval;
522         while ((*ptr != '\0') && !isspace(*ptr))
523         {
524             ptr++;
525         }
526         name = gmx_strndup(dval, ptr - dval);
527
528         while ((*ptr != '\0') && isspace(*ptr))
529         {
530             ptr++;
531         }
532
533         add_define(name, ptr);
534         sfree(name);
535         return eCPP_OK;
536     }
537
538     /* #undef statement */
539     if (strcmp(dname, "undef") == 0)
540     {
541         snew(name, strlen(dval)+1);
542         sscanf(dval, "%s", name);
543         for (i = 0; (i < ndef); i++)
544         {
545             if (strcmp(defs[i].name, name) == 0)
546             {
547                 sfree(defs[i].name);
548                 sfree(defs[i].def);
549                 break;
550             }
551         }
552         sfree(name);
553         for (; (i < ndef-1); i++)
554         {
555             defs[i].name = defs[i+1].name;
556             defs[i].def  = defs[i+1].def;
557         }
558         ndef--;
559
560         return eCPP_OK;
561     }
562
563     /* If we haven't matched anything, this is an unknown directive */
564     return eCPP_SYNTAX;
565 }
566
567 /* Return one whole line from the file into buf which holds at most n
568    characters, for subsequent processing. Returns integer status. This
569    routine also does all the "intelligent" work like processing cpp
570    directives and so on. Note that often the routine is called
571    recursively and no cpp directives are printed. */
572 int cpp_read_line(gmx_cpp_t *handlep, int n, char buf[])
573 {
574     gmx_cpp_t   handle = (gmx_cpp_t)*handlep;
575     int         i, nn, len, status;
576     const char *ptr, *ptr2;
577     char       *name;
578     char       *dname, *dval;
579     gmx_bool    bEOF;
580
581     if (!handle)
582     {
583         return eCPP_INVALID_HANDLE;
584     }
585     if (!handle->fp)
586     {
587         return eCPP_FILE_NOT_OPEN;
588     }
589
590     bEOF = feof(handle->fp);
591     if (!bEOF)
592     {
593         /* Read the actual line now. */
594         if (fgets2(buf, n-1, handle->fp) == NULL)
595         {
596             /* Recheck EOF, since we could have been at the end before
597              * the fgets2 call, but we need to read past the end to know.
598              */
599             bEOF = feof(handle->fp);
600             if (!bEOF)
601             {
602                 /* Something strange happened, fgets returned NULL,
603                  * but we are not at EOF.
604                  */
605                 return eCPP_UNKNOWN;
606             }
607         }
608     }
609
610     if (bEOF)
611     {
612         if (handle->parent == NULL)
613         {
614             return eCPP_EOF;
615         }
616         cpp_close_file(handlep);
617         *handlep      = handle->parent;
618         handle->child = NULL;
619         return cpp_read_line(handlep, n, buf);
620     }
621     else
622     {
623         if (n > handle->line_len)
624         {
625             handle->line_len = n;
626             srenew(handle->line, n);
627         }
628         strcpy(handle->line, buf);
629         handle->line_nr++;
630     }
631     /* Now we've read a line! */
632     if (debug)
633     {
634         fprintf(debug, "%s : %4d : %s\n", handle->fn, handle->line_nr, buf);
635     }
636
637     /* Process directives if this line contains one */
638     if (find_directive(buf, &dname, &dval))
639     {
640         status = process_directive(handlep, dname, dval);
641         if (status != eCPP_OK)
642         {
643             return status;
644         }
645         /* Don't print lines with directives, go on to the next */
646         return cpp_read_line(handlep, n, buf);
647     }
648
649     /* Check whether we're not ifdeffed out. The order of this statement
650        is important. It has to come after #ifdef, #else and #endif, but
651        anything else should be ignored. */
652     if (is_ifdeffed_out(handle))
653     {
654         return cpp_read_line(handlep, n, buf);
655     }
656
657     /* Check whether we have any defines that need to be replaced. Note
658        that we have to use a best fit algorithm, rather than first come
659        first go. We do this by sorting the defines on length first, and
660        then on alphabetical order. */
661     for (i = 0; (i < ndef); i++)
662     {
663         if (defs[i].def)
664         {
665             nn  = 0;
666             ptr = buf;
667             while ((ptr = strstrw(ptr, defs[i].name)) != NULL)
668             {
669                 nn++;
670                 ptr += strlen(defs[i].name);
671             }
672             if (nn > 0)
673             {
674                 size_t four = 4;
675
676                 len = strlen(buf) + nn*std::max(four, four+strlen(defs[i].def)-strlen(defs[i].name));
677                 snew(name, len);
678                 ptr = buf;
679                 while ((ptr2 = strstrw(ptr, defs[i].name)) != NULL)
680                 {
681                     strncat(name, ptr, (int)(ptr2-ptr));
682                     strcat(name, defs[i].def);
683                     ptr = ptr2 + strlen(defs[i].name);
684                 }
685                 strcat(name, ptr);
686                 strcpy(buf, name);
687                 sfree(name);
688             }
689         }
690     }
691
692     return eCPP_OK;
693 }
694
695 char *cpp_cur_file(const gmx_cpp_t *handlep)
696 {
697     return (*handlep)->fn;
698 }
699
700 int cpp_cur_linenr(const gmx_cpp_t *handlep)
701 {
702     return (*handlep)->line_nr;
703 }
704
705 /* Close the file! Return integer status. */
706 int cpp_close_file(gmx_cpp_t *handlep)
707 {
708     gmx_cpp_t handle = (gmx_cpp_t)*handlep;
709
710     if (!handle)
711     {
712         return eCPP_INVALID_HANDLE;
713     }
714     if (!handle->fp)
715     {
716         return eCPP_FILE_NOT_OPEN;
717     }
718     if (debug)
719     {
720         fprintf(debug, "GMXCPP: closing file %s\n", handle->fn);
721     }
722     fclose(handle->fp);
723     if (NULL != handle->cwd)
724     {
725         if (NULL != debug)
726         {
727             fprintf(debug, "GMXCPP: chdir to %s\n", handle->cwd);
728         }
729         gmx_chdir(handle->cwd);
730     }
731
732     if (0)
733     {
734         switch (errno)
735         {
736             case 0:
737                 break;
738             case ENOENT:
739                 return eCPP_FILE_NOT_FOUND;
740             case EBADF:
741                 return eCPP_FILE_NOT_OPEN;
742             case EINTR:
743                 return eCPP_INTERRUPT;
744             default:
745                 if (debug)
746                 {
747                     fprintf(debug, "Strange stuff closing file, errno = %d", errno);
748                 }
749                 return eCPP_UNKNOWN;
750         }
751     }
752     handle->fp      = NULL;
753     handle->line_nr = 0;
754     if (NULL != handle->fn)
755     {
756         sfree(handle->fn);
757         handle->fn = NULL;
758     }
759     if (NULL != handle->line)
760     {
761         sfree(handle->line);
762         handle->line = NULL;
763     }
764     if (NULL != handle->ifdefs)
765     {
766         sfree(handle->ifdefs);
767     }
768     handle->nifdef = 0;
769     if (NULL != handle->path)
770     {
771         sfree(handle->path);
772     }
773     if (NULL != handle->cwd)
774     {
775         sfree(handle->cwd);
776     }
777
778     return eCPP_OK;
779 }
780
781 void cpp_done()
782 {
783     done_includes();
784     done_defines();
785 }
786
787 /* Return a string containing the error message coresponding to status
788    variable */
789 char *cpp_error(gmx_cpp_t *handlep, int status)
790 {
791     char        buf[256];
792     const char *ecpp[] = {
793         "OK", "File not found", "End of file", "Syntax error", "Interrupted",
794         "Invalid file handle",
795         "File not open", "Unknown error", "Error status out of range"
796     };
797     gmx_cpp_t   handle = (gmx_cpp_t)*handlep;
798
799     if (!handle)
800     {
801         return (char *)ecpp[eCPP_INVALID_HANDLE];
802     }
803
804     if ((status < 0) || (status >= eCPP_NR))
805     {
806         status = eCPP_NR;
807     }
808
809     sprintf(buf, "%s - File %s, line %d\nLast line read:\n'%s'",
810             ecpp[status],
811             (handle && handle->fn) ? handle->fn : "unknown",
812             (handle) ? handle->line_nr : -1,
813             handle->line ? handle->line : "");
814
815     return gmx_strdup(buf);
816 }