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