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 /* Set the error flag */ 86 pp_status.state = 1; 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 /* Set the error flag */ 100 pp_status.state = 1; 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 if(!s) 114 return NULL; 115 return memcpy(s, str, len); 116 } 117 118 static char *wpp_default_lookup(const char *name, int type, const char *parent_name, 119 char **include_path, int include_path_count) 120 { 121 char *cpy; 122 char *cptr; 123 char *path; 124 const char *ccptr; 125 int i, fd; 126 127 cpy = pp_xmalloc(strlen(name)+1); 128 if(!cpy) 129 return NULL; 130 cptr = cpy; 131 132 for(ccptr = name; *ccptr; ccptr++) 133 { 134 /* Convert to forward slash */ 135 if(*ccptr == '\\') { 136 /* kill double backslash */ 137 if(ccptr[1] == '\\') 138 ccptr++; 139 *cptr = '/'; 140 }else { 141 *cptr = *ccptr; 142 } 143 cptr++; 144 } 145 *cptr = '\0'; 146 147 if(type && parent_name) 148 { 149 /* Search directory of parent include and then -I path */ 150 const char *p; 151 152 if ((p = strrchr( parent_name, '/' ))) p++; 153 else p = parent_name; 154 path = pp_xmalloc( (p - parent_name) + strlen(cpy) + 1 ); 155 if(!path) 156 { 157 free(cpy); 158 return NULL; 159 } 160 memcpy( path, parent_name, p - parent_name ); 161 strcpy( path + (p - parent_name), cpy ); 162 fd = open( path, O_RDONLY ); 163 if (fd != -1) 164 { 165 close( fd ); 166 free( cpy ); 167 return path; 168 } 169 free( path ); 170 } 171 /* Search -I path */ 172 for(i = 0; i < include_path_count; i++) 173 { 174 path = pp_xmalloc(strlen(include_path[i]) + strlen(cpy) + 2); 175 if(!path) 176 { 177 free(cpy); 178 return NULL; 179 } 180 strcpy(path, include_path[i]); 181 strcat(path, "/"); 182 strcat(path, cpy); 183 fd = open( path, O_RDONLY ); 184 if (fd != -1) 185 { 186 close( fd ); 187 free( cpy ); 188 return path; 189 } 190 free( path ); 191 } 192 free( cpy ); 193 return NULL; 194 } 195 196 static void *wpp_default_open(const char *filename, int type) { 197 return fopen(filename,"rt"); 198 } 199 200 static void wpp_default_close(void *file) { 201 fclose(file); 202 } 203 204 static int wpp_default_read(void *file, char *buffer, unsigned int len){ 205 return fread(buffer, 1, len, file); 206 } 207 208 static void wpp_default_write( const char *buffer, unsigned int len ) { 209 fwrite(buffer, 1, len, ppy_out); 210 } 211 212 /* Don't comment on the hash, it's primitive but functional... */ 213 static int pphash(const char *str) 214 { 215 int sum = 0; 216 while(*str) 217 sum += *str++; 218 return sum % HASHKEY; 219 } 220 221 pp_entry_t *pplookup(const char *ident) 222 { 223 int idx; 224 pp_entry_t *ppp; 225 226 if(!ident) 227 return NULL; 228 idx = pphash(ident); 229 for(ppp = pp_def_state->defines[idx]; ppp; ppp = ppp->next) 230 { 231 if(!strcmp(ident, ppp->ident)) 232 return ppp; 233 } 234 return NULL; 235 } 236 237 static void free_pp_entry( pp_entry_t *ppp, int idx ) 238 { 239 if(ppp->iep) 240 { 241 if(ppp->iep == pp_includelogiclist) 242 { 243 pp_includelogiclist = ppp->iep->next; 244 if(pp_includelogiclist) 245 pp_includelogiclist->prev = NULL; 246 } 247 else 248 { 249 ppp->iep->prev->next = ppp->iep->next; 250 if(ppp->iep->next) 251 ppp->iep->next->prev = ppp->iep->prev; 252 } 253 free(ppp->iep->filename); 254 free(ppp->iep); 255 } 256 257 if(pp_def_state->defines[idx] == ppp) 258 { 259 pp_def_state->defines[idx] = ppp->next; 260 if(pp_def_state->defines[idx]) 261 pp_def_state->defines[idx]->prev = NULL; 262 } 263 else 264 { 265 ppp->prev->next = ppp->next; 266 if(ppp->next) 267 ppp->next->prev = ppp->prev; 268 } 269 270 free(ppp); 271 } 272 273 /* push a new (empty) define state */ 274 int pp_push_define_state(void) 275 { 276 pp_def_state_t *state = pp_xmalloc( sizeof(*state) ); 277 if(!state) 278 return 1; 279 280 memset( state->defines, 0, sizeof(state->defines) ); 281 state->next = pp_def_state; 282 pp_def_state = state; 283 return 0; 284 } 285 286 /* pop the current define state */ 287 void pp_pop_define_state(void) 288 { 289 int i; 290 pp_entry_t *ppp; 291 pp_def_state_t *state; 292 293 for (i = 0; i < HASHKEY; i++) 294 { 295 while ((ppp = pp_def_state->defines[i]) != NULL) pp_del_define( ppp->ident ); 296 } 297 state = pp_def_state; 298 pp_def_state = state->next; 299 free( state ); 300 } 301 302 void pp_del_define(const char *name) 303 { 304 pp_entry_t *ppp; 305 int idx = pphash(name); 306 307 if((ppp = pplookup(name)) == NULL) 308 { 309 if(pp_status.pedantic) 310 ppy_warning("%s was not defined", name); 311 return; 312 } 313 314 if(pp_status.debug) 315 printf("Deleting (%s, %d) <%s>\n", pp_status.input, pp_status.line_number, name); 316 317 free( ppp->ident ); 318 free( ppp->subst.text ); 319 free( ppp->filename ); 320 free_pp_entry( ppp, idx ); 321 } 322 323 pp_entry_t *pp_add_define(const char *def, const char *text) 324 { 325 int len; 326 char *cptr; 327 int idx; 328 pp_entry_t *ppp; 329 330 if(!def) 331 return NULL; 332 idx = pphash(def); 333 if((ppp = pplookup(def)) != NULL) 334 { 335 if(pp_status.pedantic) 336 ppy_warning("Redefinition of %s\n\tPrevious definition: %s:%d", def, ppp->filename, ppp->linenumber); 337 pp_del_define(def); 338 } 339 ppp = pp_xmalloc(sizeof(pp_entry_t)); 340 if(!ppp) 341 return NULL; 342 memset( ppp, 0, sizeof(*ppp) ); 343 ppp->ident = pp_xstrdup(def); 344 if(!ppp->ident) 345 goto error; 346 ppp->type = def_define; 347 ppp->subst.text = text ? pp_xstrdup(text) : NULL; 348 if(text && !ppp->subst.text) 349 goto error; 350 ppp->filename = pp_xstrdup(pp_status.input ? pp_status.input : "<internal or cmdline>"); 351 if(!ppp->filename) 352 goto error; 353 ppp->linenumber = pp_status.input ? pp_status.line_number : 0; 354 ppp->next = pp_def_state->defines[idx]; 355 pp_def_state->defines[idx] = ppp; 356 if(ppp->next) 357 ppp->next->prev = ppp; 358 if(ppp->subst.text) 359 { 360 /* Strip trailing white space from subst text */ 361 len = strlen(ppp->subst.text); 362 while(len && strchr(" \t\r\n", ppp->subst.text[len-1])) 363 { 364 ppp->subst.text[--len] = '\0'; 365 } 366 /* Strip leading white space from subst text */ 367 for(cptr = ppp->subst.text; *cptr && strchr(" \t\r", *cptr); cptr++) 368 ; 369 if(ppp->subst.text != cptr) 370 memmove(ppp->subst.text, cptr, strlen(cptr)+1); 371 } 372 if(pp_status.debug) 373 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)"); 374 375 return ppp; 376 377 error: 378 free(ppp->ident); 379 free(ppp->subst.text); 380 free(ppp); 381 return NULL; 382 } 383 384 pp_entry_t *pp_add_macro(char *id, marg_t *args[], int nargs, mtext_t *exp) 385 { 386 int idx; 387 pp_entry_t *ppp; 388 389 if(!id) 390 return NULL; 391 idx = pphash(id); 392 if((ppp = pplookup(id)) != NULL) 393 { 394 if(pp_status.pedantic) 395 ppy_warning("Redefinition of %s\n\tPrevious definition: %s:%d", id, ppp->filename, ppp->linenumber); 396 pp_del_define(id); 397 } 398 ppp = pp_xmalloc(sizeof(pp_entry_t)); 399 if(!ppp) 400 return NULL; 401 memset( ppp, 0, sizeof(*ppp) ); 402 ppp->ident = id; 403 ppp->type = def_macro; 404 ppp->margs = args; 405 ppp->nargs = nargs; 406 ppp->subst.mtext= exp; 407 ppp->filename = pp_xstrdup(pp_status.input ? pp_status.input : "<internal or cmdline>"); 408 if(!ppp->filename) 409 { 410 free(ppp); 411 return NULL; 412 } 413 ppp->linenumber = pp_status.input ? pp_status.line_number : 0; 414 ppp->next = pp_def_state->defines[idx]; 415 pp_def_state->defines[idx] = ppp; 416 if(ppp->next) 417 ppp->next->prev = ppp; 418 419 if(pp_status.debug) 420 { 421 fprintf(stderr, "Added macro (%s, %d) <%s(%d)> to <", pp_status.input, pp_status.line_number, ppp->ident, nargs); 422 for(; exp; exp = exp->next) 423 { 424 switch(exp->type) 425 { 426 case exp_text: 427 fprintf(stderr, " \"%s\" ", exp->subst.text); 428 break; 429 case exp_stringize: 430 fprintf(stderr, " #(%d) ", exp->subst.argidx); 431 break; 432 case exp_concat: 433 fprintf(stderr, "##"); 434 break; 435 case exp_subst: 436 fprintf(stderr, " <%d> ", exp->subst.argidx); 437 break; 438 } 439 } 440 fprintf(stderr, ">\n"); 441 } 442 return ppp; 443 } 444 445 446 /* 447 *------------------------------------------------------------------------- 448 * Include management 449 *------------------------------------------------------------------------- 450 */ 451 #if defined(_WIN32) || defined(__MSDOS__) 452 #define INCLUDESEPARATOR ";" 453 #else 454 #define INCLUDESEPARATOR ":" 455 #endif 456 457 static char **includepath; 458 static int nincludepath = 0; 459 460 int wpp_add_include_path(const char *path) 461 { 462 char *tok; 463 char *cpy = pp_xstrdup(path); 464 if(!cpy) 465 return 1; 466 467 tok = strtok(cpy, INCLUDESEPARATOR); 468 while(tok) 469 { 470 if(*tok) { 471 char *dir; 472 char *cptr; 473 char **new_path; 474 475 dir = pp_xstrdup(tok); 476 if(!dir) 477 { 478 free(cpy); 479 return 1; 480 } 481 for(cptr = dir; *cptr; cptr++) 482 { 483 /* Convert to forward slash */ 484 if(*cptr == '\\') 485 *cptr = '/'; 486 } 487 /* Kill eventual trailing '/' */ 488 if(*(cptr = dir + strlen(dir)-1) == '/') 489 *cptr = '\0'; 490 491 /* Add to list */ 492 new_path = pp_xrealloc(includepath, (nincludepath+1) * sizeof(*includepath)); 493 if(!new_path) 494 { 495 free(dir); 496 free(cpy); 497 return 1; 498 } 499 includepath = new_path; 500 includepath[nincludepath] = dir; 501 nincludepath++; 502 } 503 tok = strtok(NULL, INCLUDESEPARATOR); 504 } 505 free(cpy); 506 return 0; 507 } 508 509 char *wpp_find_include(const char *name, const char *parent_name) 510 { 511 return wpp_default_lookup(name, !!parent_name, parent_name, includepath, nincludepath); 512 } 513 514 void *pp_open_include(const char *name, int type, const char *parent_name, char **newpath) 515 { 516 char *path; 517 void *fp; 518 519 if (!(path = wpp_callbacks->lookup(name, type, parent_name, includepath, 520 nincludepath))) return NULL; 521 fp = wpp_callbacks->open(path, type); 522 523 if (fp) 524 { 525 if (pp_status.debug) 526 printf("Going to include <%s>\n", path); 527 if (newpath) *newpath = path; 528 else free( path ); 529 return fp; 530 } 531 free( path ); 532 return NULL; 533 } 534 535 /* 536 *------------------------------------------------------------------------- 537 * #if, #ifdef, #ifndef, #else, #elif and #endif state management 538 * 539 * #if state transitions are made on basis of the current TOS and the next 540 * required state. The state transitions are required to housekeep because 541 * #if:s can be nested. The ignore case is activated to prevent output from 542 * within a false clause. 543 * Some special cases come from the fact that the #elif cases are not 544 * binary, but three-state. The problem is that all other elif-cases must 545 * be false when one true one has been found. A second problem is that the 546 * #else clause is a final clause. No extra #else:s may follow. 547 * 548 * The states mean: 549 * if_true Process input to output 550 * if_false Process input but no output 551 * if_ignore Process input but no output 552 * if_elif Process input but no output 553 * if_elsefalse Process input but no output 554 * if_elsettrue Process input to output 555 * 556 * The possible state-sequences are [state(stack depth)] (rest can be deduced): 557 * TOS #if 1 #else #endif 558 * if_true(n) if_true(n+1) if_elsefalse(n+1) 559 * if_false(n) if_ignore(n+1) if_ignore(n+1) 560 * if_elsetrue(n) if_true(n+1) if_elsefalse(n+1) 561 * if_elsefalse(n) if_ignore(n+1) if_ignore(n+1) 562 * if_elif(n) if_ignore(n+1) if_ignore(n+1) 563 * if_ignore(n) if_ignore(n+1) if_ignore(n+1) 564 * 565 * TOS #if 1 #elif 0 #else #endif 566 * if_true(n) if_true(n+1) if_elif(n+1) if_elif(n+1) 567 * if_false(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) 568 * if_elsetrue(n) if_true(n+1) if_elif(n+1) if_elif(n+1) 569 * if_elsefalse(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) 570 * if_elif(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) 571 * if_ignore(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) 572 * 573 * TOS #if 0 #elif 1 #else #endif 574 * if_true(n) if_false(n+1) if_true(n+1) if_elsefalse(n+1) 575 * if_false(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) 576 * if_elsetrue(n) if_false(n+1) if_true(n+1) if_elsefalse(n+1) 577 * if_elsefalse(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) 578 * if_elif(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) 579 * if_ignore(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) 580 * 581 *------------------------------------------------------------------------- 582 */ 583 static const char * const pp_if_state_str[] = { 584 "if_false", 585 "if_true", 586 "if_elif", 587 "if_elsefalse", 588 "if_elsetrue", 589 "if_ignore" 590 }; 591 592 void pp_push_if(pp_if_state_t s) 593 { 594 if(if_stack_idx >= MAXIFSTACK) 595 pp_internal_error(__FILE__, __LINE__, "#if-stack overflow; #{if,ifdef,ifndef} nested too deeply (> %d)", MAXIFSTACK); 596 597 if(pp_flex_debug) 598 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); 599 600 if_stack[if_stack_idx++] = s; 601 602 switch(s) 603 { 604 case if_true: 605 case if_elsetrue: 606 break; 607 case if_false: 608 case if_elsefalse: 609 case if_elif: 610 case if_ignore: 611 pp_push_ignore_state(); 612 break; 613 default: 614 pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d)", (int)pp_if_state()); 615 } 616 } 617 618 pp_if_state_t pp_pop_if(void) 619 { 620 if(if_stack_idx <= 0) 621 { 622 ppy_error("#{endif,else,elif} without #{if,ifdef,ifndef} (#if-stack underflow)"); 623 return if_error; 624 } 625 626 switch(pp_if_state()) 627 { 628 case if_true: 629 case if_elsetrue: 630 break; 631 case if_false: 632 case if_elsefalse: 633 case if_elif: 634 case if_ignore: 635 pp_pop_ignore_state(); 636 break; 637 default: 638 pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d)", (int)pp_if_state()); 639 } 640 641 if(pp_flex_debug) 642 fprintf(stderr, "Pop if %s:%d: %s(%d) -> %s(%d)\n", 643 pp_status.input, 644 pp_status.line_number, 645 pp_if_state_str[pp_if_state()], 646 if_stack_idx, 647 pp_if_state_str[if_stack[if_stack_idx <= 1 ? if_true : if_stack_idx-2]], 648 if_stack_idx-1); 649 650 return if_stack[--if_stack_idx]; 651 } 652 653 pp_if_state_t pp_if_state(void) 654 { 655 if(!if_stack_idx) 656 return if_true; 657 else 658 return if_stack[if_stack_idx-1]; 659 } 660 661 662 void pp_next_if_state(int i) 663 { 664 switch(pp_if_state()) 665 { 666 case if_true: 667 case if_elsetrue: 668 pp_push_if(i ? if_true : if_false); 669 break; 670 case if_false: 671 case if_elsefalse: 672 case if_elif: 673 case if_ignore: 674 pp_push_if(if_ignore); 675 break; 676 default: 677 pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #{if,ifdef,ifndef} directive", (int)pp_if_state()); 678 } 679 } 680 681 int pp_get_if_depth(void) 682 { 683 return if_stack_idx; 684 } 685 686 /* #define WANT_NEAR_INDICATION */ 687 688 static void generic_msg(const char *s, const char *t, const char *n, va_list ap) 689 { 690 fprintf(stderr, "%s:%d:%d: %s: ", pp_status.input ? pp_status.input : "stdin", 691 pp_status.line_number, pp_status.char_number, t); 692 vfprintf(stderr, s, ap); 693 #ifdef WANT_NEAR_INDICATION 694 { 695 char *cpy, *p; 696 if(n) 697 { 698 cpy = pp_xstrdup(n); 699 if(!cpy) 700 goto end; 701 for (p = cpy; *p; p++) if(!isprint(*p)) *p = ' '; 702 fprintf(stderr, " near '%s'", cpy); 703 free(cpy); 704 } 705 } 706 end: 707 #endif 708 fprintf(stderr, "\n"); 709 } 710 711 static void wpp_default_error(const char *file, int line, int col, const char *near, const char *msg, va_list ap) 712 { 713 generic_msg(msg, "Error", near, ap); 714 exit(1); 715 } 716 717 static void wpp_default_warning(const char *file, int line, int col, const char *near, const char *msg, va_list ap) 718 { 719 generic_msg(msg, "Warning", near, ap); 720 } 721 722 static const struct wpp_callbacks default_callbacks = 723 { 724 wpp_default_lookup, 725 wpp_default_open, 726 wpp_default_close, 727 wpp_default_read, 728 wpp_default_write, 729 wpp_default_error, 730 wpp_default_warning, 731 }; 732 733 const struct wpp_callbacks *wpp_callbacks = &default_callbacks; 734 735 int ppy_error(const char *s, ...) 736 { 737 va_list ap; 738 va_start(ap, s); 739 wpp_callbacks->error(pp_status.input, pp_status.line_number, pp_status.char_number, ppy_text, s, ap); 740 va_end(ap); 741 pp_status.state = 1; 742 return 1; 743 } 744 745 int ppy_warning(const char *s, ...) 746 { 747 va_list ap; 748 va_start(ap, s); 749 wpp_callbacks->warning(pp_status.input, pp_status.line_number, pp_status.char_number, ppy_text, s, ap); 750 va_end(ap); 751 return 0; 752 } 753 754 void pp_internal_error(const char *file, int line, const char *s, ...) 755 { 756 va_list ap; 757 va_start(ap, s); 758 fprintf(stderr, "Internal error (please report) %s %d: ", file, line); 759 vfprintf(stderr, s, ap); 760 fprintf(stderr, "\n"); 761 va_end(ap); 762 exit(3); 763 } 764