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