Moved all references to file descriptors out of the generic object
[wine] / tools / wpp / preproc.c
1 /*
2  * Copyright 1998 Bertho A. Stultiens (BS)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #include "config.h"
20
21 #include <assert.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30
31 #include "wpp_private.h"
32
33 struct pp_status pp_status;
34
35 #define HASHKEY         2039
36 static pp_entry_t *pp_defines[HASHKEY];
37
38 #define MAXIFSTACK      64
39 static pp_if_state_t if_stack[MAXIFSTACK];
40 static int if_stack_idx = 0;
41
42 #if 0
43 void pp_status(void) __attribute__((destructor));
44 void pp_status(void)
45 {
46         int i;
47         int sum;
48         int total = 0;
49         pp_entry_t *ppp;
50
51         fprintf(stderr, "Defines statistics:\n");
52         for(i = 0; i < HASHKEY; i++)
53         {
54                 sum = 0;
55                 for(ppp = pp_defines[i]; ppp; ppp = ppp->next)
56                         sum++;
57                 total += sum;
58                 fprintf(stderr, "%4d, %3d\n", i, sum);
59         }
60         fprintf(stderr, "Total defines: %d\n", total);
61 }
62 #endif
63
64 void *pp_xmalloc(size_t size)
65 {
66     void *res;
67
68     assert(size > 0);
69     res = malloc(size);
70     if(res == NULL)
71     {
72         fprintf(stderr, "Virtual memory exhausted.\n");
73         exit(2);
74     }
75     return res;
76 }
77
78 void *pp_xrealloc(void *p, size_t size)
79 {
80     void *res;
81
82     assert(size > 0);
83     res = realloc(p, size);
84     if(res == NULL)
85     {
86         fprintf(stderr, "Virtual memory exhausted.\n");
87         exit(2);
88     }
89     return res;
90 }
91
92 char *pp_xstrdup(const char *str)
93 {
94         char *s;
95
96         assert(str != NULL);
97         s = pp_xmalloc(strlen(str)+1);
98         return strcpy(s, str);
99 }
100
101 /* Don't comment on the hash, its primitive but functional... */
102 int pphash(char *str)
103 {
104         int sum = 0;
105         while(*str)
106                 sum += *str++;
107         return sum % HASHKEY;
108 }
109
110 pp_entry_t *pplookup(char *ident)
111 {
112         int idx = pphash(ident);
113         pp_entry_t *ppp;
114
115         for(ppp = pp_defines[idx]; ppp; ppp = ppp->next)
116         {
117                 if(!strcmp(ident, ppp->ident))
118                         return ppp;
119         }
120         return NULL;
121 }
122
123 void pp_del_define(char *name)
124 {
125         int idx;
126         pp_entry_t *ppp;
127
128         if((ppp = pplookup(name)) == NULL)
129         {
130                 if(pp_status.pedantic)
131                         ppwarning("%s was not defined", name);
132                 return;
133         }
134
135         if(ppp->iep)
136         {
137                 if(pp_status.debug)
138                         fprintf(stderr, "pp_del_define: %s:%d: includelogic removed, include_ppp='%s', file=%s\n", pp_status.input, pp_status.line_number, name, ppp->iep->filename);
139                 if(ppp->iep == pp_includelogiclist)
140                 {
141                         pp_includelogiclist = ppp->iep->next;
142                         if(pp_includelogiclist)
143                                 pp_includelogiclist->prev = NULL;
144                 }
145                 else
146                 {
147                         ppp->iep->prev->next = ppp->iep->next;
148                         if(ppp->iep->next)
149                                 ppp->iep->next->prev = ppp->iep->prev;
150                 }
151                 free(ppp->iep->filename);
152                 free(ppp->iep);
153         }
154
155         idx = pphash(name);
156         if(pp_defines[idx] == ppp)
157         {
158                 pp_defines[idx] = ppp->next;
159                 if(pp_defines[idx])
160                         pp_defines[idx]->prev = NULL;
161         }
162         else
163         {
164                 ppp->prev->next = ppp->next;
165                 if(ppp->next)
166                         ppp->next->prev = ppp->prev;
167         }
168
169         free(ppp);
170
171         if(pp_status.debug)
172                 printf("Deleted (%s, %d) <%s>\n", pp_status.input, pp_status.line_number, name);
173 }
174
175 pp_entry_t *pp_add_define(char *def, char *text)
176 {
177         int len;
178         char *cptr;
179         int idx = pphash(def);
180         pp_entry_t *ppp;
181
182         if((ppp = pplookup(def)) != NULL)
183         {
184                 if(pp_status.pedantic)
185                         ppwarning("Redefinition of %s\n\tPrevious definition: %s:%d", def, ppp->filename, ppp->linenumber);
186                 pp_del_define(def);
187         }
188         ppp = pp_xmalloc(sizeof(pp_entry_t));
189         memset( ppp, 0, sizeof(*ppp) );
190         ppp->ident = def;
191         ppp->type = def_define;
192         ppp->subst.text = text;
193         ppp->filename = pp_status.input ? pp_xstrdup(pp_status.input) : "<internal or cmdline>";
194         ppp->linenumber = pp_status.input ? pp_status.line_number : 0;
195         ppp->next = pp_defines[idx];
196         pp_defines[idx] = ppp;
197         if(ppp->next)
198                 ppp->next->prev = ppp;
199         if(text)
200         {
201                 /* Strip trailing white space from subst text */
202                 len = strlen(text);
203                 while(len && strchr(" \t\r\n", text[len-1]))
204                 {
205                         text[--len] = '\0';
206                 }
207                 /* Strip leading white space from subst text */
208                 for(cptr = text; *cptr && strchr(" \t\r", *cptr); cptr++)
209                 ;
210                 if(text != cptr)
211                         memmove(text, cptr, strlen(cptr)+1);
212         }
213         if(pp_status.debug)
214                 printf("Added define (%s, %d) <%s> to <%s>\n", pp_status.input, pp_status.line_number, ppp->ident, text ? text : "(null)");
215
216         return ppp;
217 }
218
219 pp_entry_t *pp_add_macro(char *id, marg_t *args[], int nargs, mtext_t *exp)
220 {
221         int idx = pphash(id);
222         pp_entry_t *ppp;
223
224         if((ppp = pplookup(id)) != NULL)
225         {
226                 if(pp_status.pedantic)
227                         ppwarning("Redefinition of %s\n\tPrevious definition: %s:%d", id, ppp->filename, ppp->linenumber);
228                 pp_del_define(id);
229         }
230         ppp = pp_xmalloc(sizeof(pp_entry_t));
231         memset( ppp, 0, sizeof(*ppp) );
232         ppp->ident      = id;
233         ppp->type       = def_macro;
234         ppp->margs      = args;
235         ppp->nargs      = nargs;
236         ppp->subst.mtext= exp;
237         ppp->filename = pp_status.input ? pp_xstrdup(pp_status.input) : "<internal or cmdline>";
238         ppp->linenumber = pp_status.input ? pp_status.line_number : 0;
239         ppp->next       = pp_defines[idx];
240         pp_defines[idx] = ppp;
241         if(ppp->next)
242                 ppp->next->prev = ppp;
243
244         if(pp_status.debug)
245         {
246                 fprintf(stderr, "Added macro (%s, %d) <%s(%d)> to <", pp_status.input, pp_status.line_number, ppp->ident, nargs);
247                 for(; exp; exp = exp->next)
248                 {
249                         switch(exp->type)
250                         {
251                         case exp_text:
252                                 fprintf(stderr, " \"%s\" ", exp->subst.text);
253                                 break;
254                         case exp_stringize:
255                                 fprintf(stderr, " #(%d) ", exp->subst.argidx);
256                                 break;
257                         case exp_concat:
258                                 fprintf(stderr, "##");
259                                 break;
260                         case exp_subst:
261                                 fprintf(stderr, " <%d> ", exp->subst.argidx);
262                                 break;
263                         }
264                 }
265                 fprintf(stderr, ">\n");
266         }
267         return ppp;
268 }
269
270
271 /*
272  *-------------------------------------------------------------------------
273  * Include management
274  *-------------------------------------------------------------------------
275  */
276 #if defined(_Windows) || defined(__MSDOS__)
277 #define INCLUDESEPARATOR        ";"
278 #else
279 #define INCLUDESEPARATOR        ":"
280 #endif
281
282 static char **includepath;
283 static int nincludepath = 0;
284
285 void wpp_add_include_path(const char *path)
286 {
287         char *tok;
288         char *cpy = pp_xstrdup(path);
289
290         tok = strtok(cpy, INCLUDESEPARATOR);
291         while(tok)
292         {
293                 char *dir;
294                 char *cptr;
295                 if(strlen(tok) == 0)
296                         continue;
297                 dir = pp_xstrdup(tok);
298                 for(cptr = dir; *cptr; cptr++)
299                 {
300                         /* Convert to forward slash */
301                         if(*cptr == '\\')
302                                 *cptr = '/';
303                 }
304                 /* Kill eventual trailing '/' */
305                 if(*(cptr = dir + strlen(dir)-1) == '/')
306                         *cptr = '\0';
307
308                 /* Add to list */
309                 nincludepath++;
310                 includepath = pp_xrealloc(includepath, nincludepath * sizeof(*includepath));
311                 includepath[nincludepath-1] = dir;
312                 tok = strtok(NULL, INCLUDESEPARATOR);
313         }
314         free(cpy);
315 }
316
317 char *wpp_find_include(const char *name, int search)
318 {
319     char *cpy = pp_xstrdup(name);
320     char *cptr;
321     int i, fd;
322
323     for(cptr = cpy; *cptr; cptr++)
324     {
325         /* kill double backslash */
326         if(*cptr == '\\' && *(cptr+1) == '\\')
327             memmove(cptr, cptr+1, strlen(cptr));
328         /* Convert to forward slash */
329         if(*cptr == '\\')
330             *cptr = '/';
331     }
332
333     if(search)
334     {
335         /* Search current dir and then -I path */
336         fd = open( cpy, O_RDONLY );
337         if (fd != -1)
338         {
339             close( fd );
340             return cpy;
341         }
342     }
343     /* Search -I path */
344     for(i = 0; i < nincludepath; i++)
345     {
346         char *path;
347         path = pp_xmalloc(strlen(includepath[i]) + strlen(cpy) + 2);
348         strcpy(path, includepath[i]);
349         strcat(path, "/");
350         strcat(path, cpy);
351         fd = open( path, O_RDONLY );
352         if (fd != -1)
353         {
354             close( fd );
355             free( cpy );
356             return path;
357         }
358         free( path );
359     }
360     free( cpy );
361     return NULL;
362 }
363
364 FILE *pp_open_include(const char *name, int search, char **newpath)
365 {
366     char *path;
367     FILE *fp;
368
369     if (!(path = wpp_find_include( name, search ))) return NULL;
370     fp = fopen(path, "rt");
371
372     if (fp)
373     {
374         if (pp_status.debug)
375             printf("Going to include <%s>\n", path);
376         if (newpath) *newpath = path;
377         else free( path );
378         return fp;
379     }
380     free( path );
381     return NULL;
382 }
383
384 /*
385  *-------------------------------------------------------------------------
386  * #if, #ifdef, #ifndef, #else, #elif and #endif state management
387  *
388  * #if state transitions are made on basis of the current TOS and the next
389  * required state. The state transitions are required to housekeep because
390  * #if:s can be nested. The ignore case is activated to prevent output from
391  * within a false clause.
392  * Some special cases come from the fact that the #elif cases are not
393  * binary, but three-state. The problem is that all other elif-cases must
394  * be false when one true one has been found. A second problem is that the
395  * #else clause is a final clause. No extra #else:s may follow.
396  *
397  * The states mean:
398  * if_true      Process input to output
399  * if_false     Process input but no output
400  * if_ignore    Process input but no output
401  * if_elif      Process input but no output
402  * if_elsefalse Process input but no output
403  * if_elsettrue Process input to output
404  *
405  * The possible state-sequences are [state(stack depth)] (rest can be deduced):
406  *      TOS             #if 1           #else                   #endif
407  *      if_true(n)      if_true(n+1)    if_elsefalse(n+1)
408  *      if_false(n)     if_ignore(n+1)  if_ignore(n+1)
409  *      if_elsetrue(n)  if_true(n+1)    if_elsefalse(n+1)
410  *      if_elsefalse(n) if_ignore(n+1)  if_ignore(n+1)
411  *      if_elif(n)      if_ignore(n+1)  if_ignore(n+1)
412  *      if_ignore(n)    if_ignore(n+1)  if_ignore(n+1)
413  *
414  *      TOS             #if 1           #elif 0         #else           #endif
415  *      if_true(n)      if_true(n+1)    if_elif(n+1)    if_elif(n+1)
416  *      if_false(n)     if_ignore(n+1)  if_ignore(n+1)  if_ignore(n+1)
417  *      if_elsetrue(n)  if_true(n+1)    if_elif(n+1)    if_elif(n+1)
418  *      if_elsefalse(n) if_ignore(n+1)  if_ignore(n+1)  if_ignore(n+1)
419  *      if_elif(n)      if_ignore(n+1)  if_ignore(n+1)  if_ignore(n+1)
420  *      if_ignore(n)    if_ignore(n+1)  if_ignore(n+1)  if_ignore(n+1)
421  *
422  *      TOS             #if 0           #elif 1         #else           #endif
423  *      if_true(n)      if_false(n+1)   if_true(n+1)    if_elsefalse(n+1)
424  *      if_false(n)     if_ignore(n+1)  if_ignore(n+1)  if_ignore(n+1)
425  *      if_elsetrue(n)  if_false(n+1)   if_true(n+1)    if_elsefalse(n+1)
426  *      if_elsefalse(n) if_ignore(n+1)  if_ignore(n+1)  if_ignore(n+1)
427  *      if_elif(n)      if_ignore(n+1)  if_ignore(n+1)  if_ignore(n+1)
428  *      if_ignore(n)    if_ignore(n+1)  if_ignore(n+1)  if_ignore(n+1)
429  *
430  *-------------------------------------------------------------------------
431  */
432 static char *pp_if_state_str[] = {
433         "if_false",
434         "if_true",
435         "if_elif",
436         "if_elsefalse",
437         "if_elsetrue",
438         "if_ignore"
439 };
440
441 void pp_push_if(pp_if_state_t s)
442 {
443         if(if_stack_idx >= MAXIFSTACK)
444                 pp_internal_error(__FILE__, __LINE__, "#if-stack overflow; #{if,ifdef,ifndef} nested too deeply (> %d)", MAXIFSTACK);
445
446         if(pp_flex_debug)
447                 fprintf(stderr, "Push if %s:%d: %s(%d) -> %s(%d)\n", pp_status.input, pp_status.line_number, pp_if_state_str[pp_if_state()], if_stack_idx, pp_if_state_str[s], if_stack_idx+1);
448
449         if_stack[if_stack_idx++] = s;
450
451         switch(s)
452         {
453         case if_true:
454         case if_elsetrue:
455                 break;
456         case if_false:
457         case if_elsefalse:
458         case if_elif:
459         case if_ignore:
460                 pp_push_ignore_state();
461                 break;
462         }
463 }
464
465 pp_if_state_t pp_pop_if(void)
466 {
467         if(if_stack_idx <= 0)
468                 pperror("#{endif,else,elif} without #{if,ifdef,ifndef} (#if-stack underflow)");
469
470         switch(pp_if_state())
471         {
472         case if_true:
473         case if_elsetrue:
474                 break;
475         case if_false:
476         case if_elsefalse:
477         case if_elif:
478         case if_ignore:
479                 pp_pop_ignore_state();
480                 break;
481         }
482
483         if(pp_flex_debug)
484                 fprintf(stderr, "Pop if %s:%d: %s(%d) -> %s(%d)\n",
485                                 pp_status.input,
486                                 pp_status.line_number,
487                                 pp_if_state_str[pp_if_state()],
488                                 if_stack_idx,
489                                 pp_if_state_str[if_stack[if_stack_idx <= 1 ? if_true : if_stack_idx-2]],
490                                 if_stack_idx-1);
491
492         return if_stack[--if_stack_idx];
493 }
494
495 pp_if_state_t pp_if_state(void)
496 {
497         if(!if_stack_idx)
498                 return if_true;
499         else
500                 return if_stack[if_stack_idx-1];
501 }
502
503
504 void pp_next_if_state(int i)
505 {
506         switch(pp_if_state())
507         {
508         case if_true:
509         case if_elsetrue:
510                 pp_push_if(i ? if_true : if_false);
511                 break;
512         case if_false:
513         case if_elsefalse:
514         case if_elif:
515         case if_ignore:
516                 pp_push_if(if_ignore);
517                 break;
518         default:
519                 pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #{if,ifdef,ifndef} directive", (int)pp_if_state());
520         }
521 }
522
523 int pp_get_if_depth(void)
524 {
525         return if_stack_idx;
526 }
527
528 /* #define WANT_NEAR_INDICATION */
529
530 static void generic_msg(const char *s, const char *t, const char *n, va_list ap)
531 {
532         fprintf(stderr, "%s:%d:%d: %s: ", pp_status.input ? pp_status.input : "stdin",
533                 pp_status.line_number, pp_status.char_number, t);
534         vfprintf(stderr, s, ap);
535 #ifdef WANT_NEAR_INDICATION
536         {
537                 char *cpy, *p;
538                 if(n)
539                 {
540                         cpy = pp_xstrdup(n);
541                         for (p = cpy; *p; p++) if(!isprint(*p)) *p = ' ';
542                         fprintf(stderr, " near '%s'", cpy);
543                         free(cpy);
544                 }
545         }
546 #endif
547         fprintf(stderr, "\n");
548 }
549
550 int pperror(const char *s, ...)
551 {
552         va_list ap;
553         va_start(ap, s);
554         generic_msg(s, "Error", pptext, ap);
555         va_end(ap);
556         exit(1);
557         return 1;
558 }
559
560 int ppwarning(const char *s, ...)
561 {
562         va_list ap;
563         va_start(ap, s);
564         generic_msg(s, "Warning", pptext, ap);
565         va_end(ap);
566         return 0;
567 }
568
569 void pp_internal_error(const char *file, int line, const char *s, ...)
570 {
571         va_list ap;
572         va_start(ap, s);
573         fprintf(stderr, "Internal error (please report) %s %d: ", file, line);
574         vfprintf(stderr, s, ap);
575         fprintf(stderr, "\n");
576         va_end(ap);
577         exit(3);
578 }