wpp.c 15.3 KB
Newer Older
1 2 3
/*
 * Exported functions of the Wine preprocessor
 *
4
 * Copyright 1998 Bertho A. Stultiens
5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * Copyright 2002 Alexandre Julliard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21
 */

22 23
#include "config.h"

24 25 26 27
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
28
#include <stdlib.h>
29 30 31
#include <string.h>
#include <stdarg.h>
#include <time.h>
32

33
#include "../tools.h"
34
#include "utils.h"
35 36
#include "wpp_private.h"

37 38 39 40 41 42 43 44 45 46
struct pp_status pp_status;

#define HASHKEY		2039

static struct list pp_defines[HASHKEY];

#define MAXIFSTACK	64
static pp_if_state_t if_stack[MAXIFSTACK];
static int if_stack_idx = 0;

47
int ppy_debug, pp_flex_debug;
48

49 50
struct define
{
51
    struct list    entry;
52 53 54 55
    char          *name;
    char          *value;
};

56
static struct list cmdline_defines = LIST_INIT( cmdline_defines );
57
static struct strarray includes;
58

59
static char *wpp_lookup(const char *name, int type, const char *parent_name)
60 61 62 63 64 65 66
{
    char *cpy;
    char *cptr;
    char *path;
    const char *ccptr;
    int i, fd;

67
    cpy = xmalloc(strlen(name)+1);
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
    cptr = cpy;

    for(ccptr = name; *ccptr; ccptr++)
    {
        /* Convert to forward slash */
        if(*ccptr == '\\') {
            /* kill double backslash */
            if(ccptr[1] == '\\')
                ccptr++;
            *cptr = '/';
        }else {
            *cptr = *ccptr;
        }
        cptr++;
    }
    *cptr = '\0';

    if(type && parent_name)
    {
        /* Search directory of parent include and then -I path */
88
        path = strmake( "%s/%s", get_dirname(parent_name), cpy );
89 90 91 92 93 94 95 96 97 98
        fd = open( path, O_RDONLY );
        if (fd != -1)
        {
            close( fd );
            free( cpy );
            return path;
        }
        free( path );
    }
    /* Search -I path */
99
    for(i = 0; i < includes.count; i++)
100
    {
101
        path = strmake("%s/%s", includes.str[i], cpy);
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
        fd = open( path, O_RDONLY );
        if (fd != -1)
        {
            close( fd );
            free( cpy );
            return path;
        }
        free( path );
    }
    free( cpy );
    return NULL;
}

/* Don't comment on the hash, it's primitive but functional... */
static int pphash(const char *str)
{
	int sum = 0;
	while(*str)
		sum += *str++;
	return sum % HASHKEY;
}

pp_entry_t *pplookup(const char *ident)
{
	int idx;
	pp_entry_t *ppp;

	if(!ident)
		return NULL;
	idx = pphash(ident);
        LIST_FOR_EACH_ENTRY( ppp, &pp_defines[idx], pp_entry_t, entry )
	{
		if(!strcmp(ident, ppp->ident))
			return ppp;
	}
	return NULL;
}

static void free_pp_entry( pp_entry_t *ppp, int idx )
{
	if(ppp->iep)
	{
                list_remove( &ppp->iep->entry );
		free(ppp->iep->filename);
		free(ppp->iep);
	}
        list_remove( &ppp->entry );
	free(ppp);
}

/* initialize the define state */
153
static void pp_init_define_state(void)
154 155 156 157 158 159 160
{
    int i;

    for (i = 0; i < HASHKEY; i++) list_init( &pp_defines[i] );
}

/* free the current define state */
161
static void pp_free_define_state(void)
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
{
    int i;
    pp_entry_t *ppp, *ppp2;

    for (i = 0; i < HASHKEY; i++)
    {
        LIST_FOR_EACH_ENTRY_SAFE( ppp, ppp2, &pp_defines[i], pp_entry_t, entry )
        {
            free( ppp->ident );
            free( ppp->subst.text );
            free( ppp->filename );
            free_pp_entry( ppp, i );
        }
    }
}

void pp_del_define(const char *name)
{
	pp_entry_t *ppp;
	int idx = pphash(name);

	if((ppp = pplookup(name)) == NULL)
	{
185
		if(pedantic)
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
			ppy_warning("%s was not defined", name);
		return;
	}

	if(pp_status.debug)
		printf("Deleting (%s, %d) <%s>\n", pp_status.input, pp_status.line_number, name);

	free( ppp->ident );
	free( ppp->subst.text );
	free( ppp->filename );
	free_pp_entry( ppp, idx );
}

pp_entry_t *pp_add_define(const char *def, const char *text)
{
	int len;
	char *cptr;
	int idx;
	pp_entry_t *ppp;

	idx = pphash(def);
	if((ppp = pplookup(def)) != NULL)
	{
209 210 211
		if(pedantic)
			ppy_warning("Redefinition of %s\n%s:%d: note: previous definition was here",
                                    def, ppp->filename, ppp->linenumber);
212 213
		pp_del_define(def);
	}
214
	ppp = xmalloc(sizeof(pp_entry_t));
215
	memset( ppp, 0, sizeof(*ppp) );
216
	ppp->ident = xstrdup(def);
217
	ppp->type = def_define;
218 219
	ppp->subst.text = text ? xstrdup(text) : NULL;
	ppp->filename = xstrdup(pp_status.input ? pp_status.input : "<internal or cmdline>");
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	ppp->linenumber = pp_status.input ? pp_status.line_number : 0;
        list_add_head( &pp_defines[idx], &ppp->entry );
	if(ppp->subst.text)
	{
		/* Strip trailing white space from subst text */
		len = strlen(ppp->subst.text);
		while(len && strchr(" \t\r\n", ppp->subst.text[len-1]))
		{
			ppp->subst.text[--len] = '\0';
		}
		/* Strip leading white space from subst text */
		for(cptr = ppp->subst.text; *cptr && strchr(" \t\r", *cptr); cptr++)
		;
		if(ppp->subst.text != cptr)
			memmove(ppp->subst.text, cptr, strlen(cptr)+1);
	}
	if(pp_status.debug)
		printf("Added define (%s, %d) <%s> to <%s>\n", pp_status.input, pp_status.line_number, ppp->ident, ppp->subst.text ? ppp->subst.text : "(null)");

	return ppp;
}

242
pp_entry_t *pp_add_macro(char *id, char *args[], int nargs, int variadic, mtext_t *exp)
243 244 245 246 247 248 249
{
	int idx;
	pp_entry_t *ppp;

	idx = pphash(id);
	if((ppp = pplookup(id)) != NULL)
	{
250 251 252
		if(pedantic)
			ppy_warning("Redefinition of %s\n%s:%d: note: previous definition was here",
                                    id, ppp->filename, ppp->linenumber);
253 254
		pp_del_define(id);
	}
255
	ppp = xmalloc(sizeof(pp_entry_t));
256 257 258 259 260
	memset( ppp, 0, sizeof(*ppp) );
	ppp->ident	= id;
	ppp->type	= def_macro;
	ppp->margs	= args;
	ppp->nargs	= nargs;
261
	ppp->variadic   = variadic;
262
	ppp->subst.mtext= exp;
263
	ppp->filename = xstrdup(pp_status.input ? pp_status.input : "<internal or cmdline>");
264 265 266 267
	ppp->linenumber = pp_status.input ? pp_status.line_number : 0;
        list_add_head( &pp_defines[idx], &ppp->entry );
	if(pp_status.debug)
	{
268
		fprintf(stderr, "Added macro (%s, %d) <%s(%d%s)> to <", pp_status.input, pp_status.line_number, ppp->ident, nargs, variadic ? ",va" : "");
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
		for(; exp; exp = exp->next)
		{
			switch(exp->type)
			{
			case exp_text:
				fprintf(stderr, " \"%s\" ", exp->subst.text);
				break;
			case exp_stringize:
				fprintf(stderr, " #(%d) ", exp->subst.argidx);
				break;
			case exp_concat:
				fprintf(stderr, "##");
				break;
			case exp_subst:
				fprintf(stderr, " <%d> ", exp->subst.argidx);
				break;
			}
		}
		fprintf(stderr, ">\n");
	}
	return ppp;
}


/*
 *-------------------------------------------------------------------------
 * Include management
 *-------------------------------------------------------------------------
 */
void wpp_add_include_path(const char *path)
{
300 301
	char *dir = xstrdup(path);
	char *cptr;
302

303
	for(cptr = dir; *cptr; cptr++)
304
	{
305 306 307
		/* Convert to forward slash */
		if(*cptr == '\\')
			*cptr = '/';
308
	}
309 310 311 312
	/* Kill eventual trailing '/' */
	if(*(cptr = dir + strlen(dir)-1) == '/') *cptr = '\0';

        strarray_add( &includes, dir );
313 314 315 316
}

char *wpp_find_include(const char *name, const char *parent_name)
{
317
    return wpp_lookup(name, !!parent_name, parent_name);
318 319 320 321 322 323 324
}

void *pp_open_include(const char *name, int type, const char *parent_name, char **newpath)
{
    char *path;
    void *fp;

325
    if (!(path = wpp_lookup(name, type, parent_name))) return NULL;
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
    fp = fopen(path, "rt");

    if (fp)
    {
        if (pp_status.debug)
            printf("Going to include <%s>\n", path);
        if (newpath) *newpath = path;
        else free( path );
        return fp;
    }
    free( path );
    return NULL;
}

/*
 *-------------------------------------------------------------------------
 * #if, #ifdef, #ifndef, #else, #elif and #endif state management
 *
 * #if state transitions are made on basis of the current TOS and the next
 * required state. The state transitions are required to housekeep because
 * #if:s can be nested. The ignore case is activated to prevent output from
 * within a false clause.
 * Some special cases come from the fact that the #elif cases are not
 * binary, but three-state. The problem is that all other elif-cases must
 * be false when one true one has been found. A second problem is that the
 * #else clause is a final clause. No extra #else:s may follow.
 *
 * The states mean:
 * if_true	Process input to output
 * if_false	Process input but no output
 * if_ignore	Process input but no output
 * if_elif	Process input but no output
 * if_elsefalse	Process input but no output
 * if_elsettrue	Process input to output
 *
 * The possible state-sequences are [state(stack depth)] (rest can be deduced):
 *	TOS		#if 1		#else			#endif
 *	if_true(n)	if_true(n+1)	if_elsefalse(n+1)
 *	if_false(n)	if_ignore(n+1)	if_ignore(n+1)
 *	if_elsetrue(n)	if_true(n+1)	if_elsefalse(n+1)
 *	if_elsefalse(n)	if_ignore(n+1)	if_ignore(n+1)
 *	if_elif(n)	if_ignore(n+1)	if_ignore(n+1)
 *	if_ignore(n)	if_ignore(n+1)	if_ignore(n+1)
 *
 *	TOS		#if 1		#elif 0		#else		#endif
 *	if_true(n)	if_true(n+1)	if_elif(n+1)	if_elif(n+1)
 *	if_false(n)	if_ignore(n+1)	if_ignore(n+1)	if_ignore(n+1)
 *	if_elsetrue(n)	if_true(n+1)	if_elif(n+1)	if_elif(n+1)
 *	if_elsefalse(n)	if_ignore(n+1)	if_ignore(n+1)	if_ignore(n+1)
 *	if_elif(n)	if_ignore(n+1)	if_ignore(n+1)	if_ignore(n+1)
 *	if_ignore(n)	if_ignore(n+1)	if_ignore(n+1)	if_ignore(n+1)
 *
 *	TOS		#if 0		#elif 1		#else		#endif
 *	if_true(n)	if_false(n+1)	if_true(n+1)	if_elsefalse(n+1)
 *	if_false(n)	if_ignore(n+1)	if_ignore(n+1)	if_ignore(n+1)
 *	if_elsetrue(n)	if_false(n+1)	if_true(n+1)	if_elsefalse(n+1)
 *	if_elsefalse(n)	if_ignore(n+1)	if_ignore(n+1)	if_ignore(n+1)
 *	if_elif(n)	if_ignore(n+1)	if_ignore(n+1)	if_ignore(n+1)
 *	if_ignore(n)	if_ignore(n+1)	if_ignore(n+1)	if_ignore(n+1)
 *
 *-------------------------------------------------------------------------
 */
static const char * const pp_if_state_str[] = {
	"if_false",
	"if_true",
	"if_elif",
	"if_elsefalse",
	"if_elsetrue",
	"if_ignore"
};

void pp_push_if(pp_if_state_t s)
{
	if(if_stack_idx >= MAXIFSTACK)
400
		error("#if-stack overflow; #{if,ifdef,ifndef} nested too deeply (> %d)\n", MAXIFSTACK);
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417

	if(pp_flex_debug)
		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);

	if_stack[if_stack_idx++] = s;

	switch(s)
	{
	case if_true:
	case if_elsetrue:
		break;
	case if_false:
	case if_elsefalse:
	case if_elif:
	case if_ignore:
		pp_push_ignore_state();
		break;
418 419
	case if_error:
		assert(0);
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
	}
}

pp_if_state_t pp_pop_if(void)
{
	if(if_stack_idx <= 0)
	{
		ppy_error("#{endif,else,elif} without #{if,ifdef,ifndef} (#if-stack underflow)");
		return if_error;
	}

	switch(pp_if_state())
	{
	case if_true:
	case if_elsetrue:
		break;
	case if_false:
	case if_elsefalse:
	case if_elif:
	case if_ignore:
		pp_pop_ignore_state();
		break;
442 443
	case if_error:
		assert(0);
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
	}

	if(pp_flex_debug)
		fprintf(stderr, "Pop 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[if_stack[if_stack_idx <= 1 ? if_true : if_stack_idx-2]],
				if_stack_idx-1);

	return if_stack[--if_stack_idx];
}

pp_if_state_t pp_if_state(void)
{
	if(!if_stack_idx)
		return if_true;
	else
		return if_stack[if_stack_idx-1];
}


void pp_next_if_state(int i)
{
	switch(pp_if_state())
	{
	case if_true:
	case if_elsetrue:
		pp_push_if(i ? if_true : if_false);
		break;
	case if_false:
	case if_elsefalse:
	case if_elif:
	case if_ignore:
		pp_push_if(if_ignore);
		break;
481 482
	case if_error:
		assert(0);
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
	}
}

int pp_get_if_depth(void)
{
	return if_stack_idx;
}

static void generic_msg(const char *s, const char *t, const char *n, va_list ap)
{
	fprintf(stderr, "%s:%d:%d: %s: ", pp_status.input ? pp_status.input : "stdin",
                pp_status.line_number, pp_status.char_number, t);
	vfprintf(stderr, s, ap);
	fprintf(stderr, "\n");
}

int ppy_error(const char *s, ...)
{
	va_list ap;
	va_start(ap, s);
	generic_msg(s, "error", ppy_text, ap);
	va_end(ap);
	exit(1);
}

int ppy_warning(const char *s, ...)
{
	va_list ap;
	va_start(ap, s);
	generic_msg(s, "warning", ppy_text, ap);
	va_end(ap);
	return 0;
}

517 518 519 520
static void add_cmdline_defines(void)
{
    struct define *def;

521
    LIST_FOR_EACH_ENTRY( def, &cmdline_defines, struct define, entry )
522
    {
523
        if (def->value) pp_add_define( def->name, def->value );
524 525 526
    }
}

527 528 529 530 531 532 533
static void add_special_defines(void)
{
    time_t now = time(NULL);
    pp_entry_t *ppp;
    char buf[32];

    strftime(buf, sizeof(buf), "\"%b %d %Y\"", localtime(&now));
534
    pp_add_define( "__DATE__", buf );
535 536

    strftime(buf, sizeof(buf), "\"%H:%M:%S\"", localtime(&now));
537
    pp_add_define( "__TIME__", buf );
538

539
    ppp = pp_add_define( "__FILE__", "" );
540
    ppp->type = def_special;
541

542
    ppp = pp_add_define( "__LINE__", "" );
543
    ppp->type = def_special;
544 545 546
}

/* add a define to the preprocessor list */
547
static void wpp_add_define( const char *name, const char *value )
548
{
549 550
    struct define *def;

551
    if (!value) value = "";
552

553
    LIST_FOR_EACH_ENTRY( def, &cmdline_defines, struct define, entry )
554 555 556
    {
        if (!strcmp( def->name, name ))
        {
557
            free( def->value );
558
            def->value = xstrdup(value);
559
            return;
560 561 562
        }
    }

563 564 565
    def = xmalloc( sizeof(*def) );
    def->name  = xstrdup(name);
    def->value = xstrdup(value);
566
    list_add_head( &cmdline_defines, &def->entry );
567 568 569
}


570
/* undefine a previously added definition */
571
void wpp_del_define( const char *name )
572
{
573 574
    struct define *def;

575
    LIST_FOR_EACH_ENTRY( def, &cmdline_defines, struct define, entry )
576 577 578
    {
        if (!strcmp( def->name, name ))
        {
579
            free( def->value );
580 581 582 583
            def->value = NULL;
            return;
        }
    }
584 585 586
}


587
/* add a command-line define of the form NAME=VALUE */
588
void wpp_add_cmdline_define( const char *value )
589
{
590
    char *p;
591
    char *str = xstrdup(value);
592

593
    p = strchr( str, '=' );
594
    if (p) *p++ = 0;
595 596
    wpp_add_define( str, p );
    free( str );
597 598 599 600 601 602 603
}


/* set the various debug flags */
void wpp_set_debug( int lex_debug, int parser_debug, int msg_debug )
{
    pp_flex_debug   = lex_debug;
604
    ppy_debug       = parser_debug;
605 606 607 608 609 610 611 612 613
    pp_status.debug = msg_debug;
}


/* the main preprocessor parsing loop */
int wpp_parse( const char *input, FILE *output )
{
    int ret;

614
    pp_status.input = NULL;
615 616
    pp_status.line_number = 1;
    pp_status.char_number = 1;
617

618
    pp_init_define_state();
619
    add_cmdline_defines();
620 621
    add_special_defines();

Matteo Bruni's avatar
Matteo Bruni committed
622
    if (!input) pp_status.file = stdin;
623
    else if (!(pp_status.file = fopen(input, "rt")))
624
        ppy_error("Could not open %s\n", input);
625

626
    pp_status.input = input ? xstrdup(input) : NULL;
627

628
    ppy_out = output;
629
    fprintf(ppy_out, "# 1 \"%s\" 1\n", input ? input : "");
630

631
    ret = ppy_parse();
632

633 634
    if (input)
    {
635
	fclose(pp_status.file);
636 637
	free(pp_status.input);
    }
638 639
    /* Clean if_stack, it could remain dirty on errors */
    while (pp_get_if_depth()) pp_pop_if();
640
    pp_free_define_state();
641 642
    return ret;
}