Redefine the default boolean type to gmx_bool.
[alexxy/gromacs.git] / src / gmxlib / gmxcpp.c
1 /*
2  * 
3  *                This source code is part of
4  * 
5  *                 G   R   O   M   A   C   S
6  * 
7  *          GROningen MAchine for Chemical Simulations
8  * 
9  *                        VERSION 3.2.0
10  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12  * Copyright (c) 2001-2004, The GROMACS development team,
13  * check out http://www.gromacs.org for more information.
14
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  * 
20  * If you want to redistribute modifications, please consider that
21  * scientific software is very special. Version control is crucial -
22  * bugs must be traceable. We will be happy to consider code for
23  * inclusion in the official distribution, but derived work must not
24  * be called official GROMACS. Details are found in the README & COPYING
25  * files - if they are missing, get the official version at www.gromacs.org.
26  * 
27  * To help us fund GROMACS development, we humbly ask that you cite
28  * the papers on the package - you can find them in the top README file.
29  * 
30  * For more info, check our website at http://www.gromacs.org
31  * 
32  * And Hey:
33  * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
34  */
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include <sys/types.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <math.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <limits.h>
46 #include <ctype.h>
47
48 /* Necessary for getcwd */
49 #if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
50 #include <direct.h>
51 #include <io.h>
52 #endif
53
54 #include "string2.h"
55 #include "smalloc.h"
56 #include "futil.h"
57 #include "macros.h"
58 #include "gmx_fatal.h"
59 #include "gmxcpp.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 { eifTRUE, eifFALSE, eifIGNORE, eifNR };
73
74 typedef struct gmx_cpp {
75   FILE     *fp;
76   char     *path,*cwd;
77   char     *fn;
78   int      line_len;
79   char     *line;
80   int      line_nr;
81   int      nifdef;
82   int      *ifdefs;
83   struct   gmx_cpp *child,*parent;
84 } gmx_cpp;
85
86 static gmx_bool is_word_end(char c)
87 {
88   return !(isalnum(c) || c == '_');
89 }
90
91 static const char *strstrw(const char *buf,const char *word)
92 {
93   const char *ptr;
94
95   while ((ptr = strstr(buf,word)) != NULL) {
96     /* Check if we did not find part of a longer word */
97     if (ptr && 
98         is_word_end(ptr[strlen(word)]) &&
99         (((ptr > buf) && is_word_end(ptr[-1])) || (ptr == buf)))
100       return ptr;
101       
102     buf = ptr + strlen(word);
103   }
104   return NULL;
105 }
106
107 static gmx_bool find_directive(char *buf, char **name, char **val)
108 {
109   /* Skip initial whitespace */
110   while (isspace(*buf)) ++buf;
111   /* Check if this is a directive */
112   if (*buf != '#')
113     return FALSE;
114   /* Skip the hash and any space after it */
115   ++buf;
116   while (isspace(*buf)) ++buf;
117   /* Set the name pointer and find the next space */
118   *name = buf;
119   while (*buf != 0 && !isspace(*buf)) ++buf;
120   /* Set the end of the name here, and skip any space */
121   if (*buf != 0)
122   {
123     *buf = 0;
124     ++buf;
125     while (isspace(*buf)) ++buf;
126   }
127   /* Check if anything is remaining */
128   *val = (*buf != 0) ? buf : NULL;
129   return TRUE;
130 }
131
132 static gmx_bool is_ifdeffed_out(gmx_cpp_t handle)
133 {
134   return ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE));
135 }
136
137 static void add_include(const char *include)
138 {
139   int i;
140   
141   if (include == NULL)
142     return;
143     
144   for(i=0; (i<nincl); i++)
145     if (strcmp(incl[i],include) == 0)
146       break;
147   if (i == nincl) {
148     nincl++;
149     srenew(incl,nincl);
150     incl[nincl-1] = strdup(include);
151   }
152 }
153
154 static void add_define(const char *name, const char *value)
155 {
156   int  i;
157
158   for(i=0; (i<ndef); i++) {
159     if (strcmp(defs[i].name,name) == 0) {
160       break;
161     }
162   }
163   if (i == ndef) {
164     ndef++;
165     srenew(defs,ndef);
166     i = ndef - 1;
167     defs[i].name = strdup(name);
168   }
169   else if (defs[i].def) {
170     if (debug)
171       fprintf(debug,"Overriding define %s\n",name);
172     sfree(defs[i].def);
173   }
174   if (value && strlen(value) > 0)
175     defs[i].def  = strdup(value);
176   else
177     defs[i].def  = NULL;
178 }
179
180 /* Open the file to be processed. The handle variable holds internal
181    info for the cpp emulator. Return integer status */
182 int cpp_open_file(const char *filenm,gmx_cpp_t *handle, char **cppopts)
183 {
184   gmx_cpp_t cpp;
185   char *buf,*pdum;
186   char *ptr;
187   int i;
188   unsigned int i1;
189   
190   /* First process options, they might be necessary for opening files
191      (especially include statements). */  
192   i  = 0;
193   if (cppopts) {
194     while(cppopts[i]) {
195       if (strstr(cppopts[i],"-I") == cppopts[i])
196         add_include(cppopts[i]+2);
197       if (strstr(cppopts[i],"-D") == cppopts[i])
198       {
199         /* If the option contains a =, split it into name and value. */
200         ptr = strchr(cppopts[i], '=');
201         if (ptr)
202         {
203           buf = gmx_strndup(cppopts[i] + 2, ptr - cppopts[i] - 2);
204           add_define(buf, ptr + 1);
205           sfree(buf);
206         }
207         else
208         {
209           add_define(cppopts[i] + 2, NULL);
210         }
211       }
212       i++;
213     }
214   }
215   if (debug)
216     fprintf(debug,"Added %d command line arguments",i);
217   
218   snew(cpp,1);
219   *handle      = cpp;
220   cpp->fn      = NULL;
221   /* Find the file. First check whether it is in the current directory. */
222   if (gmx_fexist(filenm))
223   {
224     cpp->fn = strdup(filenm);
225   }
226   else
227   {
228     /* If not, check all the paths given with -I. */
229     for (i = 0; i < nincl; ++i)
230     {
231       snew(buf, strlen(incl[i]) + strlen(filenm) + 2);
232       sprintf(buf, "%s%c%s", incl[i], DIR_SEPARATOR, filenm);
233       if (gmx_fexist(buf))
234       {
235         cpp->fn = buf;
236         break;
237       }
238       sfree(buf);
239     }
240     /* If still not found, check the Gromacs library search path. */
241     if (!cpp->fn)
242     {
243       cpp->fn = low_gmxlibfn(filenm, FALSE, FALSE);
244     }
245   }
246   if (!cpp->fn)
247   {
248     gmx_fatal(FARGS, "Topology include file \"%s\" not found", filenm);
249   }
250   /* If the file name has a path component, we need to change to that
251    * directory. */
252   ptr = strrchr(cpp->fn, DIR_SEPARATOR);
253   if (!ptr)
254   {
255     cpp->path = NULL;
256     cpp->cwd  = NULL;
257   }
258   else
259   {
260     cpp->path = cpp->fn;
261     *ptr      = '\0';
262     cpp->fn   = strdup(ptr+1);
263     snew(cpp->cwd,STRLEN);
264
265 #if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
266       pdum=_getcwd(cpp->cwd,STRLEN);
267       _chdir(cpp->path);
268 #else
269       pdum=getcwd(cpp->cwd,STRLEN);
270       if (-1 == chdir(cpp->path))
271         gmx_fatal(FARGS,"Can not chdir to %s when processing topology. Reason: %s",
272                   cpp->path,strerror(errno));
273
274 #endif
275       
276     if (NULL != debug)
277       fprintf(debug,"GMXCPP: chdir to %s\n",cpp->path);
278   }
279   cpp->line_len= 0;
280   cpp->line    = NULL;
281   cpp->line_nr = 0;
282   cpp->nifdef  = 0;
283   cpp->ifdefs  = NULL;
284   cpp->child   = NULL;
285   cpp->parent  = NULL;
286   if (cpp->fp == NULL) {
287     cpp->fp = fopen(cpp->fn, "r");
288   }
289   if (cpp->fp == NULL) {
290     switch(errno) {
291     case EINVAL:
292     default:
293       return eCPP_UNKNOWN;
294     }
295   }
296   return eCPP_OK;
297 }
298
299 static int
300 process_directive(gmx_cpp_t *handlep, const char *dname, const char *dval)
301 {
302   gmx_cpp_t handle = (gmx_cpp_t)*handlep;
303   int  i,i0,len,status;
304   unsigned int i1;
305   char *inc_fn,*name;
306   const char *ptr;
307   int  bIfdef,bIfndef;
308
309   /* #ifdef or ifndef statement */
310   bIfdef  = (strcmp(dname,"ifdef") == 0);
311   bIfndef = (strcmp(dname,"ifndef") == 0);
312   if (bIfdef || bIfndef) {
313     if ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE)) {
314       handle->nifdef++;
315       srenew(handle->ifdefs,handle->nifdef);
316       handle->ifdefs[handle->nifdef-1] = eifIGNORE;
317     }
318     else {
319       snew(name,strlen(dval)+1);
320       sscanf(dval,"%s",name);
321       for(i=0; (i<ndef); i++) 
322         if (strcmp(defs[i].name,name) == 0) 
323           break;
324       handle->nifdef++;
325       srenew(handle->ifdefs,handle->nifdef);
326       if ((bIfdef && (i < ndef)) || (bIfndef && (i == ndef))) 
327         handle->ifdefs[handle->nifdef-1] = eifTRUE;
328       else
329         handle->ifdefs[handle->nifdef-1] = eifFALSE;
330       sfree(name);
331     }
332     return eCPP_OK;
333   }
334   
335   /* #else statement */
336   if (strcmp(dname,"else") == 0) {
337     if (handle->nifdef <= 0)
338       return eCPP_SYNTAX;
339     if (handle->ifdefs[handle->nifdef-1] == eifTRUE)
340       handle->ifdefs[handle->nifdef-1] = eifFALSE;
341     else if (handle->ifdefs[handle->nifdef-1] == eifFALSE)
342       handle->ifdefs[handle->nifdef-1] = eifTRUE;
343     return eCPP_OK;
344   }
345   
346   /* #endif statement */
347   if (strcmp(dname,"endif") == 0) {
348     if (handle->nifdef <= 0)
349       return eCPP_SYNTAX;
350     handle->nifdef--;
351     return eCPP_OK;
352   }
353
354   /* Check whether we're not ifdeffed out. The order of this statement
355      is important. It has to come after #ifdef, #else and #endif, but
356      anything else should be ignored. */
357   if (is_ifdeffed_out(handle)) {
358     return eCPP_OK;
359   }
360   
361   /* Check for include statements */
362   if (strcmp(dname,"include") == 0) {
363     len = -1;
364     i0  = 0;
365     for(i1=0; (i1<strlen(dval)); i1++) {
366       if ((dval[i1] == '"') || (dval[i1] == '<') || (dval[i1] == '>'))  {
367         if (len == -1) {
368           i0 = i1+1;
369           len = 0;
370         }
371         else
372           break;
373       }
374       else if (len >= 0)
375         len++;
376     }
377     snew(inc_fn,len+1);
378     strncpy(inc_fn,dval+i0,len);
379     inc_fn[len] = '\0';
380     
381     if (debug)
382       fprintf(debug,"Going to open include file '%s' i0 = %d, strlen = %d\n",
383               inc_fn,i0,len);
384     /* Open include file and store it as a child in the handle structure */
385     status = cpp_open_file(inc_fn,&(handle->child),NULL);
386     sfree(inc_fn);
387     if (status != eCPP_OK) {
388       handle->child = NULL;
389       return status;
390     }
391     /* Make a linked list of open files and move on to the include file */
392     handle->child->parent = handle;
393     *handlep = handle->child;
394     handle = *handlep;
395     return eCPP_OK;
396   }
397   
398   /* #define statement */
399   if (strcmp(dname,"define") == 0) {
400     /* Split it into name and value. */
401     ptr = dval;
402     while ((*ptr != '\0') && !isspace(*ptr))
403       ptr++;
404     name = gmx_strndup(dval, ptr - dval);
405
406     while ((*ptr != '\0') && isspace(*ptr))
407       ptr++;
408
409     add_define(name, ptr);
410     sfree(name);
411     return eCPP_OK;
412   }
413   
414   /* #undef statement */
415   if (strcmp(dname,"undef") == 0) {
416     snew(name,strlen(dval)+1);
417     sscanf(dval,"%s",name);
418     for(i=0; (i<ndef); i++) {
419       if (strcmp(defs[i].name,name) == 0) {
420         sfree(defs[i].name);
421         sfree(defs[i].def);
422         break;
423       }
424     }
425     sfree(name);
426     for( ; (i<ndef-1); i++) {
427       defs[i].name = defs[i+1].name;
428       defs[i].def  = defs[i+1].def;
429     }
430     ndef--;
431     
432     return eCPP_OK;
433   }
434
435   /* If we haven't matched anything, this is an unknown directive */
436   return eCPP_SYNTAX;
437 }
438
439 /* Return one whole line from the file into buf which holds at most n
440    characters, for subsequent processing. Returns integer status. This
441    routine also does all the "intelligent" work like processing cpp
442    directives and so on. Note that often the routine is called
443    recursively and no cpp directives are printed. */
444 int cpp_read_line(gmx_cpp_t *handlep,int n,char buf[])
445 {
446   gmx_cpp_t handle = (gmx_cpp_t)*handlep;
447   int  i,nn,len,status;
448   const char *ptr, *ptr2;
449   char *name;
450   char *dname, *dval;
451   gmx_bool bEOF;
452
453   if (!handle)
454     return eCPP_INVALID_HANDLE;
455   if (!handle->fp)
456     return eCPP_FILE_NOT_OPEN;
457
458   bEOF = feof(handle->fp);
459   if (!bEOF) {
460     /* Read the actual line now. */
461     if (fgets2(buf,n-1,handle->fp) == NULL) {
462       /* Recheck EOF, since we could have been at the end before
463        * the fgets2 call, but we need to read past the end to know.
464        */
465        bEOF = feof(handle->fp);
466        if (!bEOF) {
467          /* Something strange happened, fgets returned NULL,
468           * but we are not at EOF.
469           */
470          return eCPP_UNKNOWN;
471        }
472     }
473   }
474
475   if (bEOF) {
476     if (handle->parent == NULL) {
477       return eCPP_EOF;
478     }
479     cpp_close_file(handlep);
480     *handlep = handle->parent;
481     handle->child = NULL;
482     return cpp_read_line(handlep,n,buf);
483   }
484   else {
485     if (n > handle->line_len) {
486       handle->line_len = n;
487       srenew(handle->line,n);
488     }
489     strcpy(handle->line,buf);
490     handle->line_nr++;
491   }
492   /* Now we've read a line! */
493   if (debug) 
494     fprintf(debug,"%s : %4d : %s\n",handle->fn,handle->line_nr,buf);
495
496   /* Process directives if this line contains one */
497   if (find_directive(buf, &dname, &dval))
498   {
499       status = process_directive(handlep, dname, dval);
500       if (status != eCPP_OK)
501           return status;
502         /* Don't print lines with directives, go on to the next */
503       return cpp_read_line(handlep,n,buf);
504   }
505
506   /* Check whether we're not ifdeffed out. The order of this statement
507      is important. It has to come after #ifdef, #else and #endif, but
508      anything else should be ignored. */
509   if (is_ifdeffed_out(handle)) {
510     return cpp_read_line(handlep,n,buf);
511   }
512   
513   /* Check whether we have any defines that need to be replaced. Note
514      that we have to use a best fit algorithm, rather than first come
515      first go. We do this by sorting the defines on length first, and
516      then on alphabetical order. */
517   for(i=0; (i<ndef); i++) {
518     if (defs[i].def) {
519       nn  = 0;
520       ptr = buf;
521       while ((ptr = strstrw(ptr,defs[i].name)) != NULL) {
522         nn++;
523         ptr += strlen(defs[i].name);
524       }
525       if (nn > 0) {
526         len = strlen(buf) + nn*max(4,4+strlen(defs[i].def)-strlen(defs[i].name));
527         snew(name,len);
528         ptr = buf;
529         while ((ptr2 = strstrw(ptr,defs[i].name)) != NULL) {
530           strncat(name,ptr,(int)(ptr2-ptr));
531           strcat(name,defs[i].def);
532           ptr = ptr2 + strlen(defs[i].name);
533         }
534         strcat(name,ptr);
535         strcpy(buf,name);
536         sfree(name);
537       }
538     }
539   }
540   
541   return eCPP_OK;
542 }
543
544 char *cpp_cur_file(const gmx_cpp_t *handlep)
545 {
546   return (*handlep)->fn;
547 }
548
549 int cpp_cur_linenr(const gmx_cpp_t *handlep)
550 {
551   return (*handlep)->line_nr;
552 }
553
554 /* Close the file! Return integer status. */
555 int cpp_close_file(gmx_cpp_t *handlep)
556 {
557   int i;
558   gmx_cpp_t handle = (gmx_cpp_t)*handlep;
559   
560   if (!handle)
561     return eCPP_INVALID_HANDLE;
562   if (!handle->fp)
563     return eCPP_FILE_NOT_OPEN;
564   if (debug)
565     fprintf(debug,"Closing file %s\n",handle->fn);
566   fclose(handle->fp);
567   if (NULL != handle->cwd) {
568     if (NULL != debug)
569       fprintf(debug,"GMXCPP: chdir to %s\n",handle->cwd);
570 #if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
571       _chdir(handle->cwd);
572 #else
573       if (-1 == chdir(handle->cwd))
574         gmx_fatal(FARGS,"Can not chdir to %s when processing topology: %s",
575                   handle->cwd,strerror(errno));
576 #endif
577   }
578   
579   if (0)
580     switch(errno) {
581   case 0:
582     break;
583   case ENOENT:
584     return eCPP_FILE_NOT_FOUND;
585   case EBADF:
586     return eCPP_FILE_NOT_OPEN;
587   case EINTR:
588     return eCPP_INTERRUPT;
589   default:
590     if (debug)
591       fprintf(debug,"Strange stuff closing file, errno = %d",errno);
592     return eCPP_UNKNOWN;
593   }
594   handle->fp = NULL;
595   handle->line_nr = 0;
596   if (NULL != handle->fn) {
597     sfree(handle->fn);
598     handle->fn = NULL;
599   }
600   if (NULL != handle->line) {
601     sfree(handle->line);
602     handle->line = NULL;
603   }
604   if (NULL != handle->ifdefs) 
605     sfree(handle->ifdefs);
606   handle->nifdef = 0;
607   if (NULL != handle->path)
608     sfree(handle->path);
609   if (NULL != handle->cwd)
610     sfree(handle->cwd);
611     
612   return eCPP_OK;
613 }
614
615 /* Return a string containing the error message coresponding to status
616    variable */
617 char *cpp_error(gmx_cpp_t *handlep,int status)
618 {
619   char buf[256];
620   const char *ecpp[] = {
621     "OK", "File not found", "End of file", "Syntax error", "Interrupted",
622     "Invalid file handle", 
623     "File not open", "Unknown error", "Error status out of range"
624   };
625   gmx_cpp_t handle = (gmx_cpp_t)*handlep;
626   
627   if (!handle)
628     return (char *)ecpp[eCPP_INVALID_HANDLE];
629     
630   if ((status < 0) || (status >= eCPP_NR))
631     status = eCPP_NR;
632   
633   sprintf(buf,"%s - File %s, line %d\nLast line read:\n'%s'",
634           ecpp[status],
635           (handle && handle->fn) ? handle->fn : "unknown",
636           (handle) ? handle->line_nr : -1,
637           handle->line ? handle->line : "");
638   
639   return strdup(buf);
640 }