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