Revised wording in pdb2gmx.c, hopefully clearer now.
[gromacs/rigid-bodies.git] / src / gmxlib / gmxcpp.c
blobbaa19ca37afa540940a7a184d0add148f6224c8e
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.
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.
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.
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.
30 * For more info, check our website at http://www.gromacs.org
32 * And Hey:
33 * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
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>
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
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"
61 typedef struct {
62 char *name;
63 char *def;
64 } t_define;
66 static int ndef = 0;
67 static t_define *defs = NULL;
68 static int nincl = 0;
69 static char **incl = 0;
71 /* enum used for handling ifdefs */
72 enum { eifTRUE, eifFALSE, eifIGNORE, eifNR };
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;
86 static gmx_bool is_word_end(char c)
88 return !(isalnum(c) || c == '_');
91 static const char *strstrw(const char *buf,const char *word)
93 const char *ptr;
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;
102 buf = ptr + strlen(word);
104 return NULL;
107 static gmx_bool find_directive(char *buf, char **name, char **val)
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)
123 *buf = 0;
124 ++buf;
125 while (isspace(*buf)) ++buf;
127 /* Check if anything is remaining */
128 *val = (*buf != 0) ? buf : NULL;
129 return TRUE;
132 static gmx_bool is_ifdeffed_out(gmx_cpp_t handle)
134 return ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE));
137 static void add_include(const char *include)
139 int i;
141 if (include == NULL)
142 return;
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);
154 static void add_define(const char *name, const char *value)
156 int i;
158 for(i=0; (i<ndef); i++) {
159 if (strcmp(defs[i].name,name) == 0) {
160 break;
163 if (i == ndef) {
164 ndef++;
165 srenew(defs,ndef);
166 i = ndef - 1;
167 defs[i].name = strdup(name);
169 else if (defs[i].def) {
170 if (debug)
171 fprintf(debug,"Overriding define %s\n",name);
172 sfree(defs[i].def);
174 if (value && strlen(value) > 0)
175 defs[i].def = strdup(value);
176 else
177 defs[i].def = NULL;
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)
184 gmx_cpp_t cpp;
185 char *buf,*pdum;
186 char *ptr, *ptr2;
187 int i;
188 unsigned int i1;
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])
199 /* If the option contains a =, split it into name and value. */
200 ptr = strchr(cppopts[i], '=');
201 if (ptr)
203 buf = gmx_strndup(cppopts[i] + 2, ptr - cppopts[i] - 2);
204 add_define(buf, ptr + 1);
205 sfree(buf);
207 else
209 add_define(cppopts[i] + 2, NULL);
212 i++;
215 if (debug)
216 fprintf(debug,"GMXCPP: added %d command line arguments\n",i);
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))
224 cpp->fn = strdup(filenm);
226 else
228 /* If not, check all the paths given with -I. */
229 for (i = 0; i < nincl; ++i)
231 snew(buf, strlen(incl[i]) + strlen(filenm) + 2);
232 sprintf(buf, "%s/%s", incl[i], filenm);
233 if (gmx_fexist(buf))
235 cpp->fn = buf;
236 break;
238 sfree(buf);
240 /* If still not found, check the Gromacs library search path. */
241 if (!cpp->fn)
243 cpp->fn = low_gmxlibfn(filenm, FALSE, FALSE);
246 if (!cpp->fn)
248 gmx_fatal(FARGS, "Topology include file \"%s\" not found", filenm);
250 if (NULL != debug) {
251 fprintf(debug,"GMXCPP: cpp file open %s\n",cpp->fn);
253 /* If the file name has a path component, we need to change to that
254 * directory. Note that we - just as C - always use UNIX path separators
255 * internally in include file names.
257 ptr = strrchr(cpp->fn, '/');
258 ptr2 = strrchr(cpp->fn, DIR_SEPARATOR);
260 if (ptr == NULL || (ptr2 != NULL && ptr2 > ptr))
262 ptr = ptr2;
264 if(ptr==NULL)
266 cpp->path = NULL;
267 cpp->cwd = NULL;
269 else
271 cpp->path = cpp->fn;
272 *ptr = '\0';
273 cpp->fn = strdup(ptr+1);
274 snew(cpp->cwd,STRLEN);
276 #if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
277 pdum=_getcwd(cpp->cwd,STRLEN);
278 _chdir(cpp->path);
279 #else
280 pdum=getcwd(cpp->cwd,STRLEN);
281 if (NULL != debug) {
282 fprintf(debug,"GMXCPP: cwd %s\n",cpp->cwd);
284 if (-1 == chdir(cpp->path))
285 gmx_fatal(FARGS,"Can not chdir to %s when processing topology. Reason: %s",
286 cpp->path,strerror(errno));
288 #endif
290 if (NULL != debug)
291 fprintf(debug,"GMXCPP: chdir to %s\n",cpp->path);
293 cpp->line_len= 0;
294 cpp->line = NULL;
295 cpp->line_nr = 0;
296 cpp->nifdef = 0;
297 cpp->ifdefs = NULL;
298 cpp->child = NULL;
299 cpp->parent = NULL;
300 if (cpp->fp == NULL) {
301 if (NULL != debug) {
302 fprintf(debug,"GMXCPP: opening file %s\n",cpp->fn);
304 cpp->fp = fopen(cpp->fn, "r");
306 if (cpp->fp == NULL) {
307 switch(errno) {
308 case EINVAL:
309 default:
310 return eCPP_UNKNOWN;
313 return eCPP_OK;
316 static int
317 process_directive(gmx_cpp_t *handlep, const char *dname, const char *dval)
319 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
320 int i,i0,len,status;
321 unsigned int i1;
322 char *inc_fn,*name;
323 const char *ptr;
324 int bIfdef,bIfndef;
326 /* #ifdef or ifndef statement */
327 bIfdef = (strcmp(dname,"ifdef") == 0);
328 bIfndef = (strcmp(dname,"ifndef") == 0);
329 if (bIfdef || bIfndef) {
330 if ((handle->nifdef > 0) && (handle->ifdefs[handle->nifdef-1] != eifTRUE)) {
331 handle->nifdef++;
332 srenew(handle->ifdefs,handle->nifdef);
333 handle->ifdefs[handle->nifdef-1] = eifIGNORE;
335 else {
336 snew(name,strlen(dval)+1);
337 sscanf(dval,"%s",name);
338 for(i=0; (i<ndef); i++)
339 if (strcmp(defs[i].name,name) == 0)
340 break;
341 handle->nifdef++;
342 srenew(handle->ifdefs,handle->nifdef);
343 if ((bIfdef && (i < ndef)) || (bIfndef && (i == ndef)))
344 handle->ifdefs[handle->nifdef-1] = eifTRUE;
345 else
346 handle->ifdefs[handle->nifdef-1] = eifFALSE;
347 sfree(name);
349 return eCPP_OK;
352 /* #else statement */
353 if (strcmp(dname,"else") == 0) {
354 if (handle->nifdef <= 0)
355 return eCPP_SYNTAX;
356 if (handle->ifdefs[handle->nifdef-1] == eifTRUE)
357 handle->ifdefs[handle->nifdef-1] = eifFALSE;
358 else if (handle->ifdefs[handle->nifdef-1] == eifFALSE)
359 handle->ifdefs[handle->nifdef-1] = eifTRUE;
360 return eCPP_OK;
363 /* #endif statement */
364 if (strcmp(dname,"endif") == 0) {
365 if (handle->nifdef <= 0)
366 return eCPP_SYNTAX;
367 handle->nifdef--;
368 return eCPP_OK;
371 /* Check whether we're not ifdeffed out. The order of this statement
372 is important. It has to come after #ifdef, #else and #endif, but
373 anything else should be ignored. */
374 if (is_ifdeffed_out(handle)) {
375 return eCPP_OK;
378 /* Check for include statements */
379 if (strcmp(dname,"include") == 0) {
380 len = -1;
381 i0 = 0;
382 for(i1=0; (i1<strlen(dval)); i1++) {
383 if ((dval[i1] == '"') || (dval[i1] == '<') || (dval[i1] == '>')) {
384 if (len == -1) {
385 i0 = i1+1;
386 len = 0;
388 else
389 break;
391 else if (len >= 0)
392 len++;
394 if (len == -1) {
395 return eCPP_SYNTAX;
397 snew(inc_fn,len+1);
398 strncpy(inc_fn,dval+i0,len);
399 inc_fn[len] = '\0';
401 if (debug)
402 fprintf(debug,"Going to open include file '%s' i0 = %d, strlen = %d\n",
403 inc_fn,i0,len);
404 /* Open include file and store it as a child in the handle structure */
405 status = cpp_open_file(inc_fn,&(handle->child),NULL);
406 sfree(inc_fn);
407 if (status != eCPP_OK) {
408 handle->child = NULL;
409 return status;
411 /* Make a linked list of open files and move on to the include file */
412 handle->child->parent = handle;
413 *handlep = handle->child;
414 handle = *handlep;
415 return eCPP_OK;
418 /* #define statement */
419 if (strcmp(dname,"define") == 0) {
420 /* Split it into name and value. */
421 ptr = dval;
422 while ((*ptr != '\0') && !isspace(*ptr))
423 ptr++;
424 name = gmx_strndup(dval, ptr - dval);
426 while ((*ptr != '\0') && isspace(*ptr))
427 ptr++;
429 add_define(name, ptr);
430 sfree(name);
431 return eCPP_OK;
434 /* #undef statement */
435 if (strcmp(dname,"undef") == 0) {
436 snew(name,strlen(dval)+1);
437 sscanf(dval,"%s",name);
438 for(i=0; (i<ndef); i++) {
439 if (strcmp(defs[i].name,name) == 0) {
440 sfree(defs[i].name);
441 sfree(defs[i].def);
442 break;
445 sfree(name);
446 for( ; (i<ndef-1); i++) {
447 defs[i].name = defs[i+1].name;
448 defs[i].def = defs[i+1].def;
450 ndef--;
452 return eCPP_OK;
455 /* If we haven't matched anything, this is an unknown directive */
456 return eCPP_SYNTAX;
459 /* Return one whole line from the file into buf which holds at most n
460 characters, for subsequent processing. Returns integer status. This
461 routine also does all the "intelligent" work like processing cpp
462 directives and so on. Note that often the routine is called
463 recursively and no cpp directives are printed. */
464 int cpp_read_line(gmx_cpp_t *handlep,int n,char buf[])
466 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
467 int i,nn,len,status;
468 const char *ptr, *ptr2;
469 char *name;
470 char *dname, *dval;
471 gmx_bool bEOF;
473 if (!handle)
474 return eCPP_INVALID_HANDLE;
475 if (!handle->fp)
476 return eCPP_FILE_NOT_OPEN;
478 bEOF = feof(handle->fp);
479 if (!bEOF) {
480 /* Read the actual line now. */
481 if (fgets2(buf,n-1,handle->fp) == NULL) {
482 /* Recheck EOF, since we could have been at the end before
483 * the fgets2 call, but we need to read past the end to know.
485 bEOF = feof(handle->fp);
486 if (!bEOF) {
487 /* Something strange happened, fgets returned NULL,
488 * but we are not at EOF.
490 return eCPP_UNKNOWN;
495 if (bEOF) {
496 if (handle->parent == NULL) {
497 return eCPP_EOF;
499 cpp_close_file(handlep);
500 *handlep = handle->parent;
501 handle->child = NULL;
502 return cpp_read_line(handlep,n,buf);
504 else {
505 if (n > handle->line_len) {
506 handle->line_len = n;
507 srenew(handle->line,n);
509 strcpy(handle->line,buf);
510 handle->line_nr++;
512 /* Now we've read a line! */
513 if (debug)
514 fprintf(debug,"%s : %4d : %s\n",handle->fn,handle->line_nr,buf);
516 /* Process directives if this line contains one */
517 if (find_directive(buf, &dname, &dval))
519 status = process_directive(handlep, dname, dval);
520 if (status != eCPP_OK)
521 return status;
522 /* Don't print lines with directives, go on to the next */
523 return cpp_read_line(handlep,n,buf);
526 /* Check whether we're not ifdeffed out. The order of this statement
527 is important. It has to come after #ifdef, #else and #endif, but
528 anything else should be ignored. */
529 if (is_ifdeffed_out(handle)) {
530 return cpp_read_line(handlep,n,buf);
533 /* Check whether we have any defines that need to be replaced. Note
534 that we have to use a best fit algorithm, rather than first come
535 first go. We do this by sorting the defines on length first, and
536 then on alphabetical order. */
537 for(i=0; (i<ndef); i++) {
538 if (defs[i].def) {
539 nn = 0;
540 ptr = buf;
541 while ((ptr = strstrw(ptr,defs[i].name)) != NULL) {
542 nn++;
543 ptr += strlen(defs[i].name);
545 if (nn > 0) {
546 len = strlen(buf) + nn*max(4,4+strlen(defs[i].def)-strlen(defs[i].name));
547 snew(name,len);
548 ptr = buf;
549 while ((ptr2 = strstrw(ptr,defs[i].name)) != NULL) {
550 strncat(name,ptr,(int)(ptr2-ptr));
551 strcat(name,defs[i].def);
552 ptr = ptr2 + strlen(defs[i].name);
554 strcat(name,ptr);
555 strcpy(buf,name);
556 sfree(name);
561 return eCPP_OK;
564 char *cpp_cur_file(const gmx_cpp_t *handlep)
566 return (*handlep)->fn;
569 int cpp_cur_linenr(const gmx_cpp_t *handlep)
571 return (*handlep)->line_nr;
574 /* Close the file! Return integer status. */
575 int cpp_close_file(gmx_cpp_t *handlep)
577 int i;
578 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
580 if (!handle)
581 return eCPP_INVALID_HANDLE;
582 if (!handle->fp)
583 return eCPP_FILE_NOT_OPEN;
584 if (debug)
585 fprintf(debug,"GMXCPP: closing file %s\n",handle->fn);
586 fclose(handle->fp);
587 if (NULL != handle->cwd) {
588 if (NULL != debug)
589 fprintf(debug,"GMXCPP: chdir to %s\n",handle->cwd);
590 #if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
591 _chdir(handle->cwd);
592 #else
593 if (-1 == chdir(handle->cwd))
594 gmx_fatal(FARGS,"Can not chdir to %s when processing topology: %s",
595 handle->cwd,strerror(errno));
596 #endif
599 if (0)
600 switch(errno) {
601 case 0:
602 break;
603 case ENOENT:
604 return eCPP_FILE_NOT_FOUND;
605 case EBADF:
606 return eCPP_FILE_NOT_OPEN;
607 case EINTR:
608 return eCPP_INTERRUPT;
609 default:
610 if (debug)
611 fprintf(debug,"Strange stuff closing file, errno = %d",errno);
612 return eCPP_UNKNOWN;
614 handle->fp = NULL;
615 handle->line_nr = 0;
616 if (NULL != handle->fn) {
617 sfree(handle->fn);
618 handle->fn = NULL;
620 if (NULL != handle->line) {
621 sfree(handle->line);
622 handle->line = NULL;
624 if (NULL != handle->ifdefs)
625 sfree(handle->ifdefs);
626 handle->nifdef = 0;
627 if (NULL != handle->path)
628 sfree(handle->path);
629 if (NULL != handle->cwd)
630 sfree(handle->cwd);
632 return eCPP_OK;
635 /* Return a string containing the error message coresponding to status
636 variable */
637 char *cpp_error(gmx_cpp_t *handlep,int status)
639 char buf[256];
640 const char *ecpp[] = {
641 "OK", "File not found", "End of file", "Syntax error", "Interrupted",
642 "Invalid file handle",
643 "File not open", "Unknown error", "Error status out of range"
645 gmx_cpp_t handle = (gmx_cpp_t)*handlep;
647 if (!handle)
648 return (char *)ecpp[eCPP_INVALID_HANDLE];
650 if ((status < 0) || (status >= eCPP_NR))
651 status = eCPP_NR;
653 sprintf(buf,"%s - File %s, line %d\nLast line read:\n'%s'",
654 ecpp[status],
655 (handle && handle->fn) ? handle->fn : "unknown",
656 (handle) ? handle->line_nr : -1,
657 handle->line ? handle->line : "");
659 return strdup(buf);