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