Changeset 331 for EcnlProtoTool/trunk/tcc-0.9.27/tccpp.c
- Timestamp:
- Jan 21, 2018, 12:10:09 AM (6 years ago)
- Location:
- EcnlProtoTool/trunk/tcc-0.9.27
- Files:
-
- 1 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
EcnlProtoTool/trunk/tcc-0.9.27/tccpp.c
r321 r331 25 25 26 26 ST_DATA int tok_flags; 27 /* additional informations about token */28 #define TOK_FLAG_BOL 0x0001 /* beginning of line before */29 #define TOK_FLAG_BOF 0x0002 /* beginning of file before */30 #define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */31 #define TOK_FLAG_EOF 0x0008 /* end of file */32 33 27 ST_DATA int parse_flags; 34 #define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */35 #define PARSE_FLAG_TOK_NUM 0x0002 /* return numbers instead of TOK_PPNUM */36 #define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a37 token. line feed is also38 returned at eof */39 #define PARSE_FLAG_ASM_COMMENTS 0x0008 /* '#' can be used for line comment */40 #define PARSE_FLAG_SPACES 0x0010 /* next() returns space tokens (for -E) */41 28 42 29 ST_DATA struct BufferedFile *file; … … 54 41 /* ------------------------------------------------------------------------- */ 55 42 56 static int *macro_ptr_allocated;57 static const int *unget_saved_macro_ptr;58 static int unget_saved_buffer[TOK_MAX_SIZE + 1];59 static int unget_buffer_enabled;60 43 static TokenSym *hash_ident[TOK_HASH_SIZE]; 61 44 static char token_buf[STRING_MAX_SIZE + 1]; 62 /* true if isid(c) || isnum(c) */ 63 static unsigned char isidnum_table[256-CH_EOF]; 45 static CString cstr_buf; 46 static CString macro_equal_buf; 47 static TokenString tokstr_buf; 48 static unsigned char isidnum_table[256 - CH_EOF]; 49 static int pp_debug_tok, pp_debug_symv; 50 static int pp_once; 51 static int pp_expr; 52 static int pp_counter; 53 static void tok_print(const char *msg, const int *str); 54 55 static struct TinyAlloc *toksym_alloc; 56 static struct TinyAlloc *tokstr_alloc; 57 static struct TinyAlloc *cstr_alloc; 58 59 static TokenString *macro_stack; 64 60 65 61 static const char tcc_keywords[] = … … 71 67 /* WARNING: the content of this string encodes token numbers */ 72 68 static const unsigned char tok_two_chars[] = 69 /* outdated -- gr 73 70 "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253" 74 71 "-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266"; 75 76 struct macro_level { 77 struct macro_level *prev; 78 const int *p; 72 */{ 73 '<','=', TOK_LE, 74 '>','=', TOK_GE, 75 '!','=', TOK_NE, 76 '&','&', TOK_LAND, 77 '|','|', TOK_LOR, 78 '+','+', TOK_INC, 79 '-','-', TOK_DEC, 80 '=','=', TOK_EQ, 81 '<','<', TOK_SHL, 82 '>','>', TOK_SAR, 83 '+','=', TOK_A_ADD, 84 '-','=', TOK_A_SUB, 85 '*','=', TOK_A_MUL, 86 '/','=', TOK_A_DIV, 87 '%','=', TOK_A_MOD, 88 '&','=', TOK_A_AND, 89 '^','=', TOK_A_XOR, 90 '|','=', TOK_A_OR, 91 '-','>', TOK_ARROW, 92 '.','.', TOK_TWODOTS, 93 '#','#', TOK_TWOSHARPS, 94 0 79 95 }; 80 96 81 ST_FUNC void next_nomacro(void);82 97 static void next_nomacro_spc(void); 83 static void macro_subst(84 TokenString *tok_str,85 Sym **nested_list,86 const int *macro_str,87 struct macro_level **can_read_stream88 );89 98 90 99 ST_FUNC void skip(int c) … … 101 110 102 111 /* ------------------------------------------------------------------------- */ 112 /* Custom allocator for tiny objects */ 113 114 //#define USE_TAL 115 116 #ifndef USE_TAL 117 #define tal_free(al, p) tcc_free(p) 118 #define tal_realloc(al, p, size) tcc_realloc(p, size) 119 #define tal_new(a,b,c) 120 #define tal_delete(a) 121 #else 122 #if !defined(MEM_DEBUG) 123 #define tal_free(al, p) tal_free_impl(al, p) 124 #define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size) 125 #define TAL_DEBUG_PARAMS 126 #else 127 #define TAL_DEBUG 1 128 //#define TAL_INFO 1 /* collect and dump allocators stats */ 129 #define tal_free(al, p) tal_free_impl(al, p, __FILE__, __LINE__) 130 #define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size, __FILE__, __LINE__) 131 #define TAL_DEBUG_PARAMS , const char *file, int line 132 #define TAL_DEBUG_FILE_LEN 40 133 #endif 134 135 #define TOKSYM_TAL_SIZE (768 * 1024) /* allocator for tiny TokenSym in table_ident */ 136 #define TOKSTR_TAL_SIZE (768 * 1024) /* allocator for tiny TokenString instances */ 137 #define CSTR_TAL_SIZE (256 * 1024) /* allocator for tiny CString instances */ 138 #define TOKSYM_TAL_LIMIT 256 /* prefer unique limits to distinguish allocators debug msgs */ 139 #define TOKSTR_TAL_LIMIT 128 /* 32 * sizeof(int) */ 140 #define CSTR_TAL_LIMIT 1024 141 142 typedef struct TinyAlloc { 143 unsigned limit; 144 unsigned size; 145 uint8_t *buffer; 146 uint8_t *p; 147 unsigned nb_allocs; 148 struct TinyAlloc *next, *top; 149 #ifdef TAL_INFO 150 unsigned nb_peak; 151 unsigned nb_total; 152 unsigned nb_missed; 153 uint8_t *peak_p; 154 #endif 155 } TinyAlloc; 156 157 typedef struct tal_header_t { 158 unsigned size; 159 #ifdef TAL_DEBUG 160 int line_num; /* negative line_num used for double free check */ 161 char file_name[TAL_DEBUG_FILE_LEN + 1]; 162 #endif 163 } tal_header_t; 164 165 /* ------------------------------------------------------------------------- */ 166 167 static TinyAlloc *tal_new(TinyAlloc **pal, unsigned limit, unsigned size) 168 { 169 TinyAlloc *al = tcc_mallocz(sizeof(TinyAlloc)); 170 al->p = al->buffer = tcc_malloc(size); 171 al->limit = limit; 172 al->size = size; 173 if (pal) *pal = al; 174 return al; 175 } 176 177 static void tal_delete(TinyAlloc *al) 178 { 179 TinyAlloc *next; 180 181 tail_call: 182 if (!al) 183 return; 184 #ifdef TAL_INFO 185 fprintf(stderr, "limit=%5d, size=%5g MB, nb_peak=%6d, nb_total=%8d, nb_missed=%6d, usage=%5.1f%%\n", 186 al->limit, al->size / 1024.0 / 1024.0, al->nb_peak, al->nb_total, al->nb_missed, 187 (al->peak_p - al->buffer) * 100.0 / al->size); 188 #endif 189 #ifdef TAL_DEBUG 190 if (al->nb_allocs > 0) { 191 uint8_t *p; 192 fprintf(stderr, "TAL_DEBUG: memory leak %d chunk(s) (limit= %d)\n", 193 al->nb_allocs, al->limit); 194 p = al->buffer; 195 while (p < al->p) { 196 tal_header_t *header = (tal_header_t *)p; 197 if (header->line_num > 0) { 198 fprintf(stderr, "%s:%d: chunk of %d bytes leaked\n", 199 header->file_name, header->line_num, header->size); 200 } 201 p += header->size + sizeof(tal_header_t); 202 } 203 #if MEM_DEBUG-0 == 2 204 exit(2); 205 #endif 206 } 207 #endif 208 next = al->next; 209 tcc_free(al->buffer); 210 tcc_free(al); 211 al = next; 212 goto tail_call; 213 } 214 215 static void tal_free_impl(TinyAlloc *al, void *p TAL_DEBUG_PARAMS) 216 { 217 if (!p) 218 return; 219 tail_call: 220 if (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size) { 221 #ifdef TAL_DEBUG 222 tal_header_t *header = (((tal_header_t *)p) - 1); 223 if (header->line_num < 0) { 224 fprintf(stderr, "%s:%d: TAL_DEBUG: double frees chunk from\n", 225 file, line); 226 fprintf(stderr, "%s:%d: %d bytes\n", 227 header->file_name, (int)-header->line_num, (int)header->size); 228 } else 229 header->line_num = -header->line_num; 230 #endif 231 al->nb_allocs--; 232 if (!al->nb_allocs) 233 al->p = al->buffer; 234 } else if (al->next) { 235 al = al->next; 236 goto tail_call; 237 } 238 else 239 tcc_free(p); 240 } 241 242 static void *tal_realloc_impl(TinyAlloc **pal, void *p, unsigned size TAL_DEBUG_PARAMS) 243 { 244 tal_header_t *header; 245 void *ret; 246 int is_own; 247 unsigned adj_size = (size + 3) & -4; 248 TinyAlloc *al = *pal; 249 250 tail_call: 251 is_own = (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size); 252 if ((!p || is_own) && size <= al->limit) { 253 if (al->p + adj_size + sizeof(tal_header_t) < al->buffer + al->size) { 254 header = (tal_header_t *)al->p; 255 header->size = adj_size; 256 #ifdef TAL_DEBUG 257 { int ofs = strlen(file) - TAL_DEBUG_FILE_LEN; 258 strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), TAL_DEBUG_FILE_LEN); 259 header->file_name[TAL_DEBUG_FILE_LEN] = 0; 260 header->line_num = line; } 261 #endif 262 ret = al->p + sizeof(tal_header_t); 263 al->p += adj_size + sizeof(tal_header_t); 264 if (is_own) { 265 header = (((tal_header_t *)p) - 1); 266 memcpy(ret, p, header->size); 267 #ifdef TAL_DEBUG 268 header->line_num = -header->line_num; 269 #endif 270 } else { 271 al->nb_allocs++; 272 } 273 #ifdef TAL_INFO 274 if (al->nb_peak < al->nb_allocs) 275 al->nb_peak = al->nb_allocs; 276 if (al->peak_p < al->p) 277 al->peak_p = al->p; 278 al->nb_total++; 279 #endif 280 return ret; 281 } else if (is_own) { 282 al->nb_allocs--; 283 ret = tal_realloc(*pal, 0, size); 284 header = (((tal_header_t *)p) - 1); 285 memcpy(ret, p, header->size); 286 #ifdef TAL_DEBUG 287 header->line_num = -header->line_num; 288 #endif 289 return ret; 290 } 291 if (al->next) { 292 al = al->next; 293 } else { 294 TinyAlloc *bottom = al, *next = al->top ? al->top : al; 295 296 al = tal_new(pal, next->limit, next->size * 2); 297 al->next = next; 298 bottom->top = al; 299 } 300 goto tail_call; 301 } 302 if (is_own) { 303 al->nb_allocs--; 304 ret = tcc_malloc(size); 305 header = (((tal_header_t *)p) - 1); 306 memcpy(ret, p, header->size); 307 #ifdef TAL_DEBUG 308 header->line_num = -header->line_num; 309 #endif 310 } else if (al->next) { 311 al = al->next; 312 goto tail_call; 313 } else 314 ret = tcc_realloc(p, size); 315 #ifdef TAL_INFO 316 al->nb_missed++; 317 #endif 318 return ret; 319 } 320 321 #endif /* USE_TAL */ 322 323 /* ------------------------------------------------------------------------- */ 103 324 /* CString handling */ 104 325 static void cstr_realloc(CString *cstr, int new_size) 105 326 { 106 327 int size; 107 void *data;108 328 109 329 size = cstr->size_allocated; 110 if (size == 0)330 if (size < 8) 111 331 size = 8; /* no need to allocate a too small first string */ 112 332 while (size < new_size) 113 333 size = size * 2; 114 data = tcc_realloc(cstr->data_allocated, size); 115 cstr->data_allocated = data; 334 cstr->data = tal_realloc(cstr_alloc, cstr->data, size); 116 335 cstr->size_allocated = size; 117 cstr->data = data;118 336 } 119 337 120 338 /* add a byte */ 121 ST_ FUNCvoid cstr_ccat(CString *cstr, int ch)339 ST_INLN void cstr_ccat(CString *cstr, int ch) 122 340 { 123 341 int size; … … 129 347 } 130 348 131 ST_FUNC void cstr_cat(CString *cstr, const char *str )132 { 133 int c;134 for(;;) {135 c = *str;136 if (c == '\0')137 break;138 cstr_ ccat(cstr, c);139 str++;140 }349 ST_FUNC void cstr_cat(CString *cstr, const char *str, int len) 350 { 351 int size; 352 if (len <= 0) 353 len = strlen(str) + 1 + len; 354 size = cstr->size + len; 355 if (size > cstr->size_allocated) 356 cstr_realloc(cstr, size); 357 memmove(((unsigned char *)cstr->data) + cstr->size, str, len); 358 cstr->size = size; 141 359 } 142 360 … … 160 378 ST_FUNC void cstr_free(CString *cstr) 161 379 { 162 t cc_free(cstr->data_allocated);380 tal_free(cstr_alloc, cstr->data); 163 381 cstr_new(cstr); 164 382 } … … 199 417 200 418 if (tok_ident >= SYM_FIRST_ANOM) 201 tcc_error("memory full ");419 tcc_error("memory full (symbols)"); 202 420 203 421 /* expand token table if needed */ … … 208 426 } 209 427 210 ts = t cc_malloc(sizeof(TokenSym) + len);428 ts = tal_realloc(toksym_alloc, 0, sizeof(TokenSym) + len); 211 429 table_ident[i] = ts; 212 430 ts->tok = tok_ident++; … … 224 442 225 443 #define TOK_HASH_INIT 1 226 #define TOK_HASH_FUNC(h, c) ((h) * 263 + (c)) 444 #define TOK_HASH_FUNC(h, c) ((h) + ((h) << 5) + ((h) >> 27) + (c)) 445 227 446 228 447 /* find a token and add it if not found */ … … 252 471 /* XXX: buffer overflow */ 253 472 /* XXX: float tokens */ 254 ST_FUNC char *get_tok_str(int v, CValue *cv) 255 { 256 static char buf[STRING_MAX_SIZE + 1]; 257 static CString cstr_buf; 258 CString *cstr; 473 ST_FUNC const char *get_tok_str(int v, CValue *cv) 474 { 259 475 char *p; 260 476 int i, len; 261 477 262 /* NOTE: to go faster, we give a fixed buffer for small strings */263 478 cstr_reset(&cstr_buf); 264 cstr_buf.data = buf; 265 cstr_buf.size_allocated = sizeof(buf); 266 p = buf; 479 p = cstr_buf.data; 267 480 268 481 switch(v) { 269 482 case TOK_CINT: 270 483 case TOK_CUINT: 271 /* XXX: not quite exact, but only useful for testing */ 272 sprintf(p, "%u", cv->ui); 273 break; 484 case TOK_CLONG: 485 case TOK_CULONG: 274 486 case TOK_CLLONG: 275 487 case TOK_CULLONG: 276 488 /* XXX: not quite exact, but only useful for testing */ 277 489 #ifdef _WIN32 278 sprintf(p, "%u", (unsigned)cv-> ull);490 sprintf(p, "%u", (unsigned)cv->i); 279 491 #else 280 sprintf(p, "% Lu", cv->ull);492 sprintf(p, "%llu", (unsigned long long)cv->i); 281 493 #endif 282 494 break; … … 290 502 break; 291 503 case TOK_PPNUM: 292 cstr = cv->cstr; 293 len = cstr->size - 1; 294 for(i=0;i<len;i++) 295 add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]); 296 cstr_ccat(&cstr_buf, '\0'); 297 break; 504 case TOK_PPSTR: 505 return (char*)cv->str.data; 298 506 case TOK_LSTR: 299 507 cstr_ccat(&cstr_buf, 'L'); 300 508 case TOK_STR: 301 cstr = cv->cstr;302 509 cstr_ccat(&cstr_buf, '\"'); 303 510 if (v == TOK_STR) { 304 len = c str->size - 1;511 len = cv->str.size - 1; 305 512 for(i=0;i<len;i++) 306 add_char(&cstr_buf, ((unsigned char *)c str->data)[i]);513 add_char(&cstr_buf, ((unsigned char *)cv->str.data)[i]); 307 514 } else { 308 len = (c str->size / sizeof(nwchar_t)) - 1;515 len = (cv->str.size / sizeof(nwchar_t)) - 1; 309 516 for(i=0;i<len;i++) 310 add_char(&cstr_buf, ((nwchar_t *)c str->data)[i]);517 add_char(&cstr_buf, ((nwchar_t *)cv->str.data)[i]); 311 518 } 312 519 cstr_ccat(&cstr_buf, '\"'); 313 520 cstr_ccat(&cstr_buf, '\0'); 314 521 break; 522 523 case TOK_CFLOAT: 524 cstr_cat(&cstr_buf, "<float>", 0); 525 break; 526 case TOK_CDOUBLE: 527 cstr_cat(&cstr_buf, "<double>", 0); 528 break; 529 case TOK_CLDOUBLE: 530 cstr_cat(&cstr_buf, "<long double>", 0); 531 break; 532 case TOK_LINENUM: 533 cstr_cat(&cstr_buf, "<linenumber>", 0); 534 break; 535 536 /* above tokens have value, the ones below don't */ 315 537 case TOK_LT: 316 538 v = '<'; … … 325 547 case TOK_A_SAR: 326 548 return strcpy(p, ">>="); 549 case TOK_EOF: 550 return strcpy(p, "<eof>"); 327 551 default: 328 552 if (v < TOK_IDENT) { … … 334 558 *p++ = q[1]; 335 559 *p = '\0'; 336 return buf;560 return cstr_buf.data; 337 561 } 338 562 q += 3; 339 563 } 564 if (v >= 127) { 565 sprintf(cstr_buf.data, "<%02x>", v); 566 return cstr_buf.data; 567 } 340 568 addv: 341 569 *p++ = v; … … 355 583 } 356 584 357 /* fill input buffer and peek next char */ 358 static int tcc_peekc_slow(BufferedFile *bf) 359 { 585 /* return the current character, handling end of block if necessary 586 (but not stray) */ 587 ST_FUNC int handle_eob(void) 588 { 589 BufferedFile *bf = file; 360 590 int len; 591 361 592 /* only tries to read if really end of buffer */ 362 593 if (bf->buf_ptr >= bf->buf_end) { 363 if (bf->fd != -1) {594 if (bf->fd >= 0) { 364 595 #if defined(PARSE_DEBUG) 365 len = 8;596 len = 1; 366 597 #else 367 598 len = IO_BUF_SIZE; … … 384 615 return CH_EOF; 385 616 } 386 }387 388 /* return the current character, handling end of block if necessary389 (but not stray) */390 ST_FUNC int handle_eob(void)391 {392 return tcc_peekc_slow(file);393 617 } 394 618 … … 436 660 int c; 437 661 662 file->buf_ptr = p; 438 663 if (p >= file->buf_end) { 439 file->buf_ptr = p;440 664 c = handle_eob(); 665 if (c != '\\') 666 return c; 441 667 p = file->buf_ptr; 442 if (c == '\\') 443 goto parse_stray; 444 } else { 445 parse_stray: 446 file->buf_ptr = p; 447 ch = *p; 448 handle_stray(); 449 p = file->buf_ptr; 450 c = *p; 451 } 668 } 669 ch = *p; 670 if (handle_stray_noerror()) { 671 if (!(parse_flags & PARSE_FLAG_ACCEPT_STRAYS)) 672 tcc_error("stray '\\' in program"); 673 *--file->buf_ptr = '\\'; 674 } 675 p = file->buf_ptr; 676 c = *p; 452 677 return c; 453 678 } … … 485 710 handle_stray(); 486 711 } 487 488 712 489 713 /* single line C++ comments */ … … 528 752 { 529 753 int c; 530 754 531 755 p++; 532 756 for(;;) { … … 558 782 c = handle_eob(); 559 783 p = file->buf_ptr; 784 if (c == CH_EOF) 785 tcc_error("unexpected end of file in comment"); 560 786 if (c == '\\') { 561 787 /* skip '\[\r]\n', otherwise just skip the stray */ … … 598 824 } 599 825 826 ST_FUNC int set_idnum(int c, int val) 827 { 828 int prev = isidnum_table[c - CH_EOF]; 829 isidnum_table[c - CH_EOF] = val; 830 return prev; 831 } 832 600 833 #define cinp minp 601 834 602 835 static inline void skip_spaces(void) 603 836 { 604 while (is _space(ch))837 while (isidnum_table[ch - CH_EOF] & IS_SPC) 605 838 cinp(); 606 839 } … … 608 841 static inline int check_space(int t, int *spc) 609 842 { 610 if ( is_space(t)) {843 if (t < 256 && (isidnum_table[t - CH_EOF] & IS_SPC)) { 611 844 if (*spc) 612 845 return 1; … … 756 989 else if (tok == TOK_LINEFEED) 757 990 goto redo_start; 758 } 991 else if (parse_flags & PARSE_FLAG_ASM_FILE) 992 p = parse_line_comment(p - 1); 993 } else if (parse_flags & PARSE_FLAG_ASM_FILE) 994 p = parse_line_comment(p - 1); 759 995 break; 760 996 _default: … … 769 1005 } 770 1006 771 /* ParseState handling */ 772 773 /* XXX: currently, no include file info is stored. Thus, we cannot display 774 accurate messages if the function or data definition spans multiple 775 files */ 776 777 /* save current parse state in 's' */ 778 ST_FUNC void save_parse_state(ParseState *s) 779 { 780 s->line_num = file->line_num; 781 s->macro_ptr = macro_ptr; 782 s->tok = tok; 783 s->tokc = tokc; 784 } 785 786 /* restore parse state from 's' */ 787 ST_FUNC void restore_parse_state(ParseState *s) 788 { 789 file->line_num = s->line_num; 790 macro_ptr = s->macro_ptr; 791 tok = s->tok; 792 tokc = s->tokc; 793 } 794 1007 #if 0 795 1008 /* return the number of additional 'ints' necessary to store the 796 1009 token */ 797 static inline int tok_ ext_size(int t)798 { 799 switch( t) {1010 static inline int tok_size(const int *p) 1011 { 1012 switch(*p) { 800 1013 /* 4 bytes */ 801 1014 case TOK_CINT: … … 805 1018 case TOK_CFLOAT: 806 1019 case TOK_LINENUM: 807 return 1 ;1020 return 1 + 1; 808 1021 case TOK_STR: 809 1022 case TOK_LSTR: 810 1023 case TOK_PPNUM: 811 tcc_error("unsupported token"); 812 return 1; 1024 case TOK_PPSTR: 1025 return 1 + ((sizeof(CString) + ((CString *)(p+1))->size + 3) >> 2); 1026 case TOK_CLONG: 1027 case TOK_CULONG: 1028 return 1 + LONG_SIZE / 4; 813 1029 case TOK_CDOUBLE: 814 1030 case TOK_CLLONG: 815 1031 case TOK_CULLONG: 816 return 2;1032 return 1 + 2; 817 1033 case TOK_CLDOUBLE: 818 return LDOUBLE_SIZE / 4;1034 return 1 + LDOUBLE_SIZE / 4; 819 1035 default: 820 return 0; 821 } 822 } 1036 return 1 + 0; 1037 } 1038 } 1039 #endif 823 1040 824 1041 /* token string handling */ 825 826 1042 ST_INLN void tok_str_new(TokenString *s) 827 1043 { 828 1044 s->str = NULL; 829 s->len = 0;1045 s->len = s->lastlen = 0; 830 1046 s->allocated_len = 0; 831 1047 s->last_line_num = -1; 832 1048 } 833 1049 834 ST_FUNC void tok_str_free(int *str) 835 { 836 tcc_free(str); 837 } 838 839 static int *tok_str_realloc(TokenString *s) 840 { 841 int *str, len; 842 843 if (s->allocated_len == 0) { 844 len = 8; 845 } else { 846 len = s->allocated_len * 2; 847 } 848 str = tcc_realloc(s->str, len * sizeof(int)); 849 s->allocated_len = len; 850 s->str = str; 1050 ST_FUNC TokenString *tok_str_alloc(void) 1051 { 1052 TokenString *str = tal_realloc(tokstr_alloc, 0, sizeof *str); 1053 tok_str_new(str); 851 1054 return str; 1055 } 1056 1057 ST_FUNC int *tok_str_dup(TokenString *s) 1058 { 1059 int *str; 1060 1061 str = tal_realloc(tokstr_alloc, 0, s->len * sizeof(int)); 1062 memcpy(str, s->str, s->len * sizeof(int)); 1063 return str; 1064 } 1065 1066 ST_FUNC void tok_str_free_str(int *str) 1067 { 1068 tal_free(tokstr_alloc, str); 1069 } 1070 1071 ST_FUNC void tok_str_free(TokenString *str) 1072 { 1073 tok_str_free_str(str->str); 1074 tal_free(tokstr_alloc, str); 1075 } 1076 1077 ST_FUNC int *tok_str_realloc(TokenString *s, int new_size) 1078 { 1079 int *str, size; 1080 1081 size = s->allocated_len; 1082 if (size < 16) 1083 size = 16; 1084 while (size < new_size) 1085 size = size * 2; 1086 if (size > s->allocated_len) { 1087 str = tal_realloc(tokstr_alloc, s->str, size * sizeof(int)); 1088 s->allocated_len = size; 1089 s->str = str; 1090 } 1091 return s->str; 852 1092 } 853 1093 … … 859 1099 str = s->str; 860 1100 if (len >= s->allocated_len) 861 str = tok_str_realloc(s );1101 str = tok_str_realloc(s, len + 1); 862 1102 str[len++] = t; 863 1103 s->len = len; 864 1104 } 865 1105 1106 ST_FUNC void begin_macro(TokenString *str, int alloc) 1107 { 1108 str->alloc = alloc; 1109 str->prev = macro_stack; 1110 str->prev_ptr = macro_ptr; 1111 str->save_line_num = file->line_num; 1112 macro_ptr = str->str; 1113 macro_stack = str; 1114 } 1115 1116 ST_FUNC void end_macro(void) 1117 { 1118 TokenString *str = macro_stack; 1119 macro_stack = str->prev; 1120 macro_ptr = str->prev_ptr; 1121 file->line_num = str->save_line_num; 1122 if (str->alloc == 2) { 1123 str->alloc = 3; /* just mark as finished */ 1124 } else { 1125 tok_str_free(str); 1126 } 1127 } 1128 866 1129 static void tok_str_add2(TokenString *s, int t, CValue *cv) 867 1130 { 868 1131 int len, *str; 869 1132 870 len = s->l en;1133 len = s->lastlen = s->len; 871 1134 str = s->str; 872 1135 873 1136 /* allocate space for worst case */ 874 if (len + TOK_MAX_SIZE > s->allocated_len)875 str = tok_str_realloc(s );1137 if (len + TOK_MAX_SIZE >= s->allocated_len) 1138 str = tok_str_realloc(s, len + TOK_MAX_SIZE + 1); 876 1139 str[len++] = t; 877 1140 switch(t) { … … 882 1145 case TOK_CFLOAT: 883 1146 case TOK_LINENUM: 1147 #if LONG_SIZE == 4 1148 case TOK_CLONG: 1149 case TOK_CULONG: 1150 #endif 884 1151 str[len++] = cv->tab[0]; 885 1152 break; 886 1153 case TOK_PPNUM: 1154 case TOK_PPSTR: 887 1155 case TOK_STR: 888 1156 case TOK_LSTR: 889 1157 { 890 int nb_words; 891 CString *cstr; 892 893 nb_words = (sizeof(CString) + cv->cstr->size + 3) >> 2; 894 while ((len + nb_words) > s->allocated_len) 895 str = tok_str_realloc(s); 896 cstr = (CString *)(str + len); 897 cstr->data = NULL; 898 cstr->size = cv->cstr->size; 899 cstr->data_allocated = NULL; 900 cstr->size_allocated = cstr->size; 901 memcpy((char *)cstr + sizeof(CString), 902 cv->cstr->data, cstr->size); 1158 /* Insert the string into the int array. */ 1159 size_t nb_words = 1160 1 + (cv->str.size + sizeof(int) - 1) / sizeof(int); 1161 if (len + nb_words >= s->allocated_len) 1162 str = tok_str_realloc(s, len + nb_words + 1); 1163 str[len] = cv->str.size; 1164 memcpy(&str[len + 1], cv->str.data, cv->str.size); 903 1165 len += nb_words; 904 1166 } … … 907 1169 case TOK_CLLONG: 908 1170 case TOK_CULLONG: 1171 #if LONG_SIZE == 8 1172 case TOK_CLONG: 1173 case TOK_CULONG: 1174 #endif 909 1175 #if LDOUBLE_SIZE == 8 910 1176 case TOK_CLDOUBLE: … … 957 1223 tab = cv->tab; 958 1224 switch(*t = *p++) { 1225 #if LONG_SIZE == 4 1226 case TOK_CLONG: 1227 #endif 959 1228 case TOK_CINT: 960 case TOK_CUINT:961 1229 case TOK_CCHAR: 962 1230 case TOK_LCHAR: 1231 case TOK_LINENUM: 1232 cv->i = *p++; 1233 break; 1234 #if LONG_SIZE == 4 1235 case TOK_CULONG: 1236 #endif 1237 case TOK_CUINT: 1238 cv->i = (unsigned)*p++; 1239 break; 963 1240 case TOK_CFLOAT: 964 case TOK_LINENUM: 965 tab[0] = *p++; 966 break; 1241 tab[0] = *p++; 1242 break; 967 1243 case TOK_STR: 968 1244 case TOK_LSTR: 969 1245 case TOK_PPNUM: 970 cv->cstr = (CString *)p; 971 cv->cstr->data = (char *)p + sizeof(CString); 972 p += (sizeof(CString) + cv->cstr->size + 3) >> 2; 1246 case TOK_PPSTR: 1247 cv->str.size = *p++; 1248 cv->str.data = p; 1249 p += (cv->str.size + sizeof(int) - 1) / sizeof(int); 973 1250 break; 974 1251 case TOK_CDOUBLE: 975 1252 case TOK_CLLONG: 976 1253 case TOK_CULLONG: 1254 #if LONG_SIZE == 8 1255 case TOK_CLONG: 1256 case TOK_CULONG: 1257 #endif 977 1258 n = 2; 978 1259 goto copy; … … 1000 1281 static int macro_is_equal(const int *a, const int *b) 1001 1282 { 1002 char buf[STRING_MAX_SIZE + 1];1003 1283 CValue cv; 1004 1284 int t; 1285 1286 if (!a || !b) 1287 return 1; 1288 1005 1289 while (*a && *b) { 1290 /* first time preallocate macro_equal_buf, next time only reset position to start */ 1291 cstr_reset(¯o_equal_buf); 1006 1292 TOK_GET(&t, &a, &cv); 1007 pstrcpy(buf, sizeof buf, get_tok_str(t, &cv));1293 cstr_cat(¯o_equal_buf, get_tok_str(t, &cv), 0); 1008 1294 TOK_GET(&t, &b, &cv); 1009 if (strcmp( buf, get_tok_str(t, &cv)))1295 if (strcmp(macro_equal_buf.data, get_tok_str(t, &cv))) 1010 1296 return 0; 1011 1297 } … … 1016 1302 ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg) 1017 1303 { 1018 Sym *s; 1019 1020 s = define_find(v); 1021 if (s && !macro_is_equal(s->d, str)) 1022 tcc_warning("%s redefined", get_tok_str(v, NULL)); 1023 1304 Sym *s, *o; 1305 1306 o = define_find(v); 1024 1307 s = sym_push2(&define_stack, v, macro_type, 0); 1025 1308 s->d = str; 1026 1309 s->next = first_arg; 1027 1310 table_ident[v - TOK_IDENT]->sym_define = s; 1311 1312 if (o && !macro_is_equal(o->d, s->d)) 1313 tcc_warning("%s redefined", get_tok_str(v, NULL)); 1028 1314 } 1029 1315 … … 1031 1317 ST_FUNC void define_undef(Sym *s) 1032 1318 { 1033 int v; 1034 v = s->v; 1319 int v = s->v; 1035 1320 if (v >= TOK_IDENT && v < tok_ident) 1036 1321 table_ident[v - TOK_IDENT]->sym_define = NULL; 1037 s->v = 0;1038 1322 } 1039 1323 … … 1049 1333 ST_FUNC void free_defines(Sym *b) 1050 1334 { 1051 Sym *top, *top1; 1052 int v; 1053 1054 top = define_stack; 1055 while (top != b) { 1056 top1 = top->prev; 1057 /* do not free args or predefined defines */ 1058 if (top->d) 1059 tok_str_free(top->d); 1060 v = top->v; 1061 if (v >= TOK_IDENT && v < tok_ident) 1062 table_ident[v - TOK_IDENT]->sym_define = NULL; 1335 while (define_stack != b) { 1336 Sym *top = define_stack; 1337 define_stack = top->prev; 1338 tok_str_free_str(top->d); 1339 define_undef(top); 1063 1340 sym_free(top); 1064 top = top1; 1065 } 1066 define_stack = b; 1341 } 1342 1343 /* restore remaining (-D or predefined) symbols if they were 1344 #undef'd in the file */ 1345 while (b) { 1346 int v = b->v; 1347 if (v >= TOK_IDENT && v < tok_ident) { 1348 Sym **d = &table_ident[v - TOK_IDENT]->sym_define; 1349 if (!*d) 1350 *d = b; 1351 } 1352 b = b->prev; 1353 } 1067 1354 } 1068 1355 … … 1095 1382 /* pop labels until element last is reached. Look if any labels are 1096 1383 undefined. Define symbols if '&&label' was used. */ 1097 ST_FUNC void label_pop(Sym **ptop, Sym *slast )1384 ST_FUNC void label_pop(Sym **ptop, Sym *slast, int keep) 1098 1385 { 1099 1386 Sym *s, *s1; … … 1114 1401 /* remove label */ 1115 1402 table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok; 1116 sym_free(s); 1117 } 1118 *ptop = slast; 1403 if (!keep) 1404 sym_free(s); 1405 } 1406 if (!keep) 1407 *ptop = slast; 1408 } 1409 1410 /* fake the nth "#if defined test_..." for tcc -dt -run */ 1411 static void maybe_run_test(TCCState *s) 1412 { 1413 const char *p; 1414 if (s->include_stack_ptr != s->include_stack) 1415 return; 1416 p = get_tok_str(tok, NULL); 1417 if (0 != memcmp(p, "test_", 5)) 1418 return; 1419 if (0 != --s->run_test) 1420 return; 1421 fprintf(s->ppfp, "\n[%s]\n" + !(s->dflag & 32), p), fflush(s->ppfp); 1422 define_push(tok, MACRO_OBJ, NULL, NULL); 1119 1423 } 1120 1424 … … 1123 1427 { 1124 1428 int c, t; 1125 TokenString str;1429 TokenString *str; 1126 1430 1127 tok_str_new(&str); 1431 str = tok_str_alloc(); 1432 pp_expr = 1; 1128 1433 while (tok != TOK_LINEFEED && tok != TOK_EOF) { 1129 1434 next(); /* do macro subst */ … … 1133 1438 if (t == '(') 1134 1439 next_nomacro(); 1440 if (tok < TOK_IDENT) 1441 expect("identifier"); 1442 if (tcc_state->run_test) 1443 maybe_run_test(tcc_state); 1135 1444 c = define_find(tok) != 0; 1136 if (t == '(') 1445 if (t == '(') { 1137 1446 next_nomacro(); 1447 if (tok != ')') 1448 expect("')'"); 1449 } 1138 1450 tok = TOK_CINT; 1139 1451 tokc.i = c; … … 1143 1455 tokc.i = 0; 1144 1456 } 1145 tok_str_add_tok(&str); 1146 } 1147 tok_str_add(&str, -1); /* simulate end of file */ 1148 tok_str_add(&str, 0); 1457 tok_str_add_tok(str); 1458 } 1459 pp_expr = 0; 1460 tok_str_add(str, -1); /* simulate end of file */ 1461 tok_str_add(str, 0); 1149 1462 /* now evaluate C constant expression */ 1150 macro_ptr = str.str;1463 begin_macro(str, 1); 1151 1464 next(); 1152 1465 c = expr_const(); 1153 macro_ptr = NULL; 1154 tok_str_free(str.str); 1466 end_macro(); 1155 1467 return c != 0; 1156 1468 } 1157 1469 1158 #if defined(PARSE_DEBUG) || defined(PP_DEBUG)1159 static void tok_print(int *str)1160 {1161 int t;1162 CValue cval;1163 1164 printf("<");1165 while (1) {1166 TOK_GET(&t, &str, &cval);1167 if (!t)1168 break;1169 printf("%s", get_tok_str(t, &cval));1170 }1171 printf(">\n");1172 }1173 #endif1174 1470 1175 1471 /* parse after #define */ … … 1178 1474 Sym *s, *first, **ps; 1179 1475 int v, t, varg, is_vaargs, spc; 1180 TokenString str;1181 1476 int saved_parse_flags = parse_flags; 1477 1182 1478 v = tok; 1183 if (v < TOK_IDENT )1479 if (v < TOK_IDENT || v == TOK_DEFINED) 1184 1480 tcc_error("invalid macro name '%s'", get_tok_str(tok, &tokc)); 1185 1481 /* XXX: should check if same macro (ANSI) */ 1186 1482 first = NULL; 1187 1483 t = MACRO_OBJ; 1484 /* We have to parse the whole define as if not in asm mode, in particular 1485 no line comment with '#' must be ignored. Also for function 1486 macros the argument list must be parsed without '.' being an ID 1487 character. */ 1488 parse_flags = ((parse_flags & ~PARSE_FLAG_ASM_FILE) | PARSE_FLAG_SPACES); 1188 1489 /* '(' must be just after macro definition for MACRO_FUNC */ 1189 1490 next_nomacro_spc(); 1190 1491 if (tok == '(') { 1492 int dotid = set_idnum('.', 0); 1191 1493 next_nomacro(); 1192 1494 ps = &first; 1193 while (tok != ')') {1495 if (tok != ')') for (;;) { 1194 1496 varg = tok; 1195 1497 next_nomacro(); … … 1203 1505 } 1204 1506 if (varg < TOK_IDENT) 1205 tcc_error("badly punctuated parameter list"); 1507 bad_list: 1508 tcc_error("bad macro parameter list"); 1206 1509 s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0); 1207 1510 *ps = s; 1208 1511 ps = &s->next; 1209 if (tok != ',')1512 if (tok == ')') 1210 1513 break; 1514 if (tok != ',' || is_vaargs) 1515 goto bad_list; 1211 1516 next_nomacro(); 1212 1517 } 1213 if (tok == ')') 1214 next_nomacro_spc(); 1518 next_nomacro_spc(); 1215 1519 t = MACRO_FUNC; 1216 } 1217 tok_str_new(&str); 1520 set_idnum('.', dotid); 1521 } 1522 1523 tokstr_buf.len = 0; 1218 1524 spc = 2; 1219 /* EOF testing necessary for '-D' handling */ 1525 parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED; 1526 /* The body of a macro definition should be parsed such that identifiers 1527 are parsed like the file mode determines (i.e. with '.' being an 1528 ID character in asm mode). But '#' should be retained instead of 1529 regarded as line comment leader, so still don't set ASM_FILE 1530 in parse_flags. */ 1220 1531 while (tok != TOK_LINEFEED && tok != TOK_EOF) { 1221 /* remove spaces around ## and after '#' */ 1532 /* remove spaces around ## and after '#' */ 1222 1533 if (TOK_TWOSHARPS == tok) { 1534 if (2 == spc) 1535 goto bad_twosharp; 1223 1536 if (1 == spc) 1224 --str.len; 1225 spc = 2; 1537 --tokstr_buf.len; 1538 spc = 3; 1539 tok = TOK_PPJOIN; 1226 1540 } else if ('#' == tok) { 1227 spc = 2;1541 spc = 4; 1228 1542 } else if (check_space(tok, &spc)) { 1229 1543 goto skip; 1230 1544 } 1231 tok_str_add2(& str, tok, &tokc);1545 tok_str_add2(&tokstr_buf, tok, &tokc); 1232 1546 skip: 1233 1547 next_nomacro_spc(); 1234 1548 } 1549 1550 parse_flags = saved_parse_flags; 1235 1551 if (spc == 1) 1236 --str.len; /* remove trailing space */ 1237 tok_str_add(&str, 0); 1238 #ifdef PP_DEBUG 1239 printf("define %s %d: ", get_tok_str(v, NULL), t); 1240 tok_print(str.str); 1241 #endif 1242 define_push(v, t, str.str, first); 1243 } 1244 1245 static inline int hash_cached_include(const char *filename) 1552 --tokstr_buf.len; /* remove trailing space */ 1553 tok_str_add(&tokstr_buf, 0); 1554 if (3 == spc) 1555 bad_twosharp: 1556 tcc_error("'##' cannot appear at either end of macro"); 1557 define_push(v, t, tok_str_dup(&tokstr_buf), first); 1558 } 1559 1560 static CachedInclude *search_cached_include(TCCState *s1, const char *filename, int add) 1246 1561 { 1247 1562 const unsigned char *s; 1248 1563 unsigned int h; 1564 CachedInclude *e; 1565 int i; 1249 1566 1250 1567 h = TOK_HASH_INIT; 1251 s = filename;1568 s = (unsigned char *) filename; 1252 1569 while (*s) { 1570 #ifdef _WIN32 1571 h = TOK_HASH_FUNC(h, toup(*s)); 1572 #else 1253 1573 h = TOK_HASH_FUNC(h, *s); 1574 #endif 1254 1575 s++; 1255 1576 } 1256 1577 h &= (CACHED_INCLUDES_HASH_SIZE - 1); 1257 return h; 1258 } 1259 1260 static CachedInclude *search_cached_include(TCCState *s1, const char *filename) 1261 { 1262 CachedInclude *e; 1263 int i, h; 1264 h = hash_cached_include(filename); 1578 1265 1579 i = s1->cached_includes_hash[h]; 1266 1580 for(;;) { … … 1272 1586 i = e->hash_next; 1273 1587 } 1274 return NULL; 1275 } 1276 1277 static inline void add_cached_include(TCCState *s1, const char *filename, int ifndef_macro) 1278 { 1279 CachedInclude *e; 1280 int h; 1281 1282 if (search_cached_include(s1, filename)) 1283 return; 1284 #ifdef INC_DEBUG 1285 printf("adding cached '%s' %s\n", filename, get_tok_str(ifndef_macro, NULL)); 1286 #endif 1588 if (!add) 1589 return NULL; 1590 1287 1591 e = tcc_malloc(sizeof(CachedInclude) + strlen(filename)); 1288 1592 strcpy(e->filename, filename); 1289 e->ifndef_macro = ifndef_macro;1290 dynarray_add( (void ***)&s1->cached_includes, &s1->nb_cached_includes, e);1593 e->ifndef_macro = e->once = 0; 1594 dynarray_add(&s1->cached_includes, &s1->nb_cached_includes, e); 1291 1595 /* add in hash table */ 1292 h = hash_cached_include(filename);1293 1596 e->hash_next = s1->cached_includes_hash[h]; 1294 1597 s1->cached_includes_hash[h] = s1->nb_cached_includes; 1598 #ifdef INC_DEBUG 1599 printf("adding cached '%s'\n", filename); 1600 #endif 1601 return e; 1295 1602 } 1296 1603 1297 1604 static void pragma_parse(TCCState *s1) 1298 1605 { 1299 int val; 1300 1301 next(); 1302 if (tok == TOK_pack) { 1303 /* 1304 This may be: 1305 #pragma pack(1) // set 1306 #pragma pack() // reset to default 1307 #pragma pack(push,1) // push & set 1308 #pragma pack(pop) // restore previous 1309 */ 1606 next_nomacro(); 1607 if (tok == TOK_push_macro || tok == TOK_pop_macro) { 1608 int t = tok, v; 1609 Sym *s; 1610 1611 if (next(), tok != '(') 1612 goto pragma_err; 1613 if (next(), tok != TOK_STR) 1614 goto pragma_err; 1615 v = tok_alloc(tokc.str.data, tokc.str.size - 1)->tok; 1616 if (next(), tok != ')') 1617 goto pragma_err; 1618 if (t == TOK_push_macro) { 1619 while (NULL == (s = define_find(v))) 1620 define_push(v, 0, NULL, NULL); 1621 s->type.ref = s; /* set push boundary */ 1622 } else { 1623 for (s = define_stack; s; s = s->prev) 1624 if (s->v == v && s->type.ref == s) { 1625 s->type.ref = NULL; 1626 break; 1627 } 1628 } 1629 if (s) 1630 table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL; 1631 else 1632 tcc_warning("unbalanced #pragma pop_macro"); 1633 pp_debug_tok = t, pp_debug_symv = v; 1634 1635 } else if (tok == TOK_once) { 1636 search_cached_include(s1, file->filename, 1)->once = pp_once; 1637 1638 } else if (s1->output_type == TCC_OUTPUT_PREPROCESS) { 1639 /* tcc -E: keep pragmas below unchanged */ 1640 unget_tok(' '); 1641 unget_tok(TOK_PRAGMA); 1642 unget_tok('#'); 1643 unget_tok(TOK_LINEFEED); 1644 1645 } else if (tok == TOK_pack) { 1646 /* This may be: 1647 #pragma pack(1) // set 1648 #pragma pack() // reset to default 1649 #pragma pack(push,1) // push & set 1650 #pragma pack(pop) // restore previous */ 1310 1651 next(); 1311 1652 skip('('); … … 1318 1659 s1->pack_stack_ptr--; 1319 1660 } else { 1320 val = 0;1661 int val = 0; 1321 1662 if (tok != ')') { 1322 1663 if (tok == TOK_ASM_push) { … … 1327 1668 skip(','); 1328 1669 } 1329 if (tok != TOK_CINT) { 1330 pack_error: 1331 tcc_error("invalid pack pragma"); 1332 } 1670 if (tok != TOK_CINT) 1671 goto pragma_err; 1333 1672 val = tokc.i; 1334 1673 if (val < 1 || val > 16 || (val & (val - 1)) != 0) 1335 goto p ack_error;1674 goto pragma_err; 1336 1675 next(); 1337 1676 } 1338 1677 *s1->pack_stack_ptr = val; 1339 skip(')'); 1340 } 1341 } 1678 } 1679 if (tok != ')') 1680 goto pragma_err; 1681 1682 } else if (tok == TOK_comment) { 1683 char *p; int t; 1684 next(); 1685 skip('('); 1686 t = tok; 1687 next(); 1688 skip(','); 1689 if (tok != TOK_STR) 1690 goto pragma_err; 1691 p = tcc_strdup((char *)tokc.str.data); 1692 next(); 1693 if (tok != ')') 1694 goto pragma_err; 1695 if (t == TOK_lib) { 1696 dynarray_add(&s1->pragma_libs, &s1->nb_pragma_libs, p); 1697 } else { 1698 if (t == TOK_option) 1699 tcc_set_options(s1, p); 1700 tcc_free(p); 1701 } 1702 1703 } else if (s1->warn_unsupported) { 1704 tcc_warning("#pragma %s is ignored", get_tok_str(tok, &tokc)); 1705 } 1706 return; 1707 1708 pragma_err: 1709 tcc_error("malformed #pragma directive"); 1710 return; 1342 1711 } 1343 1712 … … 1351 1720 1352 1721 saved_parse_flags = parse_flags; 1353 parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | 1354 PARSE_FLAG_LINEFEED; 1722 parse_flags = PARSE_FLAG_PREPROCESS 1723 | PARSE_FLAG_TOK_NUM 1724 | PARSE_FLAG_TOK_STR 1725 | PARSE_FLAG_LINEFEED 1726 | (parse_flags & PARSE_FLAG_ASM_FILE) 1727 ; 1728 1355 1729 next_nomacro(); 1356 1730 redo: 1357 1731 switch(tok) { 1358 1732 case TOK_DEFINE: 1733 pp_debug_tok = tok; 1359 1734 next_nomacro(); 1735 pp_debug_symv = tok; 1360 1736 parse_define(); 1361 1737 break; 1362 1738 case TOK_UNDEF: 1739 pp_debug_tok = tok; 1363 1740 next_nomacro(); 1741 pp_debug_symv = tok; 1364 1742 s = define_find(tok); 1365 1743 /* undefine symbol by putting an invalid name */ … … 1398 1776 #endif 1399 1777 } else { 1400 /* computed #include : either we have only strings or 1401 we have anything enclosed in '<>' */ 1778 int len; 1779 /* computed #include : concatenate everything up to linefeed, 1780 the result must be one of the two accepted forms. 1781 Don't convert pp-tokens to tokens here. */ 1782 parse_flags = (PARSE_FLAG_PREPROCESS 1783 | PARSE_FLAG_LINEFEED 1784 | (parse_flags & PARSE_FLAG_ASM_FILE)); 1402 1785 next(); 1403 1786 buf[0] = '\0'; 1404 if (tok == TOK_STR) { 1405 while (tok != TOK_LINEFEED) { 1406 if (tok != TOK_STR) { 1407 include_syntax: 1408 tcc_error("'#include' expects \"FILENAME\" or <FILENAME>"); 1409 } 1410 pstrcat(buf, sizeof(buf), (char *)tokc.cstr->data); 1411 next(); 1412 } 1413 c = '\"'; 1414 } else { 1415 int len; 1416 while (tok != TOK_LINEFEED) { 1417 pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc)); 1418 next(); 1419 } 1420 len = strlen(buf); 1421 /* check syntax and remove '<>' */ 1422 if (len < 2 || buf[0] != '<' || buf[len - 1] != '>') 1423 goto include_syntax; 1424 memmove(buf, buf + 1, len - 2); 1425 buf[len - 2] = '\0'; 1426 c = '>'; 1427 } 1787 while (tok != TOK_LINEFEED) { 1788 pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc)); 1789 next(); 1790 } 1791 len = strlen(buf); 1792 /* check syntax and remove '<>|""' */ 1793 if ((len < 2 || ((buf[0] != '"' || buf[len-1] != '"') && 1794 (buf[0] != '<' || buf[len-1] != '>')))) 1795 tcc_error("'#include' expects \"FILENAME\" or <FILENAME>"); 1796 c = buf[len-1]; 1797 memmove(buf, buf + 1, len - 2); 1798 buf[len - 2] = '\0'; 1428 1799 } 1429 1800 … … 1432 1803 /* store current file in stack, but increment stack later below */ 1433 1804 *s1->include_stack_ptr = file; 1434 1435 n = s1->nb_include_paths + s1->nb_sysinclude_paths;1436 for ( i = -2; i < n; ++i) {1805 i = tok == TOK_INCLUDE_NEXT ? file->include_next_index : 0; 1806 n = 2 + s1->nb_include_paths + s1->nb_sysinclude_paths; 1807 for (; i < n; ++i) { 1437 1808 char buf1[sizeof file->filename]; 1438 1809 CachedInclude *e; 1439 BufferedFile **f;1440 1810 const char *path; 1441 1811 1442 if (i == -2) {1812 if (i == 0) { 1443 1813 /* check absolute include path */ 1444 1814 if (!IS_ABSPATH(buf)) 1445 1815 continue; 1446 1816 buf1[0] = 0; 1447 i = n; /* force end loop */ 1448 1449 } else if (i == -1) { 1450 /* search in current dir if "header.h" */ 1817 1818 } else if (i == 1) { 1819 /* search in file's dir if "header.h" */ 1451 1820 if (c != '\"') 1452 1821 continue; 1453 path = file->filename; 1822 /* https://savannah.nongnu.org/bugs/index.php?50847 */ 1823 path = file->true_filename; 1454 1824 pstrncpy(buf1, path, tcc_basename(path) - path); 1455 1825 1456 1826 } else { 1457 1827 /* search in all the include paths */ 1458 if (i < s1->nb_include_paths) 1459 path = s1->include_paths[i]; 1460 else 1461 path = s1->sysinclude_paths[i - s1->nb_include_paths]; 1828 int j = i - 2, k = j - s1->nb_include_paths; 1829 path = k < 0 ? s1->include_paths[j] : s1->sysinclude_paths[k]; 1462 1830 pstrcpy(buf1, sizeof(buf1), path); 1463 1831 pstrcat(buf1, sizeof(buf1), "/"); … … 1465 1833 1466 1834 pstrcat(buf1, sizeof(buf1), buf); 1467 1468 if (tok == TOK_INCLUDE_NEXT) 1469 for (f = s1->include_stack_ptr; f >= s1->include_stack; --f) 1470 if (0 == PATHCMP((*f)->filename, buf1)) { 1471 #ifdef INC_DEBUG 1472 printf("%s: #include_next skipping %s\n", file->filename, buf1); 1473 #endif 1474 goto include_trynext; 1475 } 1476 1477 e = search_cached_include(s1, buf1); 1478 if (e && define_find(e->ifndef_macro)) { 1835 e = search_cached_include(s1, buf1, 0); 1836 if (e && (define_find(e->ifndef_macro) || e->once == pp_once)) { 1479 1837 /* no need to parse the include because the 'ifndef macro' 1480 is defined */1838 is defined (or had #pragma once) */ 1481 1839 #ifdef INC_DEBUG 1482 1840 printf("%s: skipping cached %s\n", file->filename, buf1); … … 1486 1844 1487 1845 if (tcc_open(s1, buf1) < 0) 1488 include_trynext:1489 1846 continue; 1490 1847 1848 file->include_next_index = i + 1; 1491 1849 #ifdef INC_DEBUG 1492 1850 printf("%s: including %s\n", file->prev->filename, file->filename); 1493 1851 #endif 1494 1852 /* update target deps */ 1495 dynarray_add( (void ***)&s1->target_deps, &s1->nb_target_deps,1853 dynarray_add(&s1->target_deps, &s1->nb_target_deps, 1496 1854 tcc_strdup(buf1)); 1497 1855 /* push current file in stack */ … … 1530 1888 do_if: 1531 1889 if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE) 1532 tcc_error("memory full ");1890 tcc_error("memory full (ifdef)"); 1533 1891 *s1->ifdef_stack_ptr++ = c; 1534 1892 goto test_skip; … … 1547 1905 tcc_error("#elif after #else"); 1548 1906 /* last #if/#elif expression was true: we skip */ 1549 if (c == 1) 1550 goto skip; 1551 c = expr_preprocess(); 1552 s1->ifdef_stack_ptr[-1] = c; 1907 if (c == 1) { 1908 c = 0; 1909 } else { 1910 c = expr_preprocess(); 1911 s1->ifdef_stack_ptr[-1] = c; 1912 } 1553 1913 test_else: 1554 1914 if (s1->ifdef_stack_ptr == file->ifdef_stack_ptr + 1) … … 1556 1916 test_skip: 1557 1917 if (!(c & 1)) { 1558 skip:1559 1918 preprocess_skip(); 1560 1919 is_bof = 0; … … 1580 1939 } 1581 1940 break; 1941 case TOK_PPNUM: 1942 n = strtoul((char*)tokc.str.data, &q, 10); 1943 goto _line_num; 1582 1944 case TOK_LINE: 1583 1945 next(); 1584 1946 if (tok != TOK_CINT) 1585 tcc_error("#line"); 1586 file->line_num = tokc.i - 1; /* the line number will be incremented after */ 1947 _line_err: 1948 tcc_error("wrong #line format"); 1949 n = tokc.i; 1950 _line_num: 1587 1951 next(); 1588 1952 if (tok != TOK_LINEFEED) { 1589 if (tok != TOK_STR) 1590 tcc_error("#line"); 1591 pstrcpy(file->filename, sizeof(file->filename), 1592 (char *)tokc.cstr->data); 1593 } 1953 if (tok == TOK_STR) { 1954 if (file->true_filename == file->filename) 1955 file->true_filename = tcc_strdup(file->filename); 1956 pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.str.data); 1957 } else if (parse_flags & PARSE_FLAG_ASM_FILE) 1958 break; 1959 else 1960 goto _line_err; 1961 --n; 1962 } 1963 if (file->fd > 0) 1964 total_lines += file->line_num - n; 1965 file->line_num = n; 1966 if (s1->do_debug) 1967 put_stabs(file->filename, N_BINCL, 0, 0, 0); 1594 1968 break; 1595 1969 case TOK_ERROR: … … 1617 1991 pragma_parse(s1); 1618 1992 break; 1993 case TOK_LINEFEED: 1994 goto the_end; 1619 1995 default: 1620 if (tok == TOK_LINEFEED || tok == '!' || tok == TOK_PPNUM) { 1621 /* '!' is ignored to allow C scripts. numbers are ignored 1622 to emulate cpp behaviour */ 1623 } else { 1624 if (!(saved_parse_flags & PARSE_FLAG_ASM_COMMENTS)) 1625 tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc)); 1626 else { 1627 /* this is a gas line comment in an 'S' file. */ 1628 file->buf_ptr = parse_line_comment(file->buf_ptr); 1629 goto the_end; 1630 } 1631 } 1632 break; 1996 /* ignore gas line comment in an 'S' file. */ 1997 if (saved_parse_flags & PARSE_FLAG_ASM_FILE) 1998 goto ignore; 1999 if (tok == '!' && is_bof) 2000 /* '!' is ignored at beginning to allow C scripts. */ 2001 goto ignore; 2002 tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc)); 2003 ignore: 2004 file->buf_ptr = parse_line_comment(file->buf_ptr - 1); 2005 goto the_end; 1633 2006 } 1634 2007 /* ignore other preprocess commands or #! for C scripts */ … … 1731 2104 break; 1732 2105 } 2106 } else if (is_long && c >= 0x80) { 2107 /* assume we are processing UTF-8 sequence */ 2108 /* reference: The Unicode Standard, Version 10.0, ch3.9 */ 2109 2110 int cont; /* count of continuation bytes */ 2111 int skip; /* how many bytes should skip when error occurred */ 2112 int i; 2113 2114 /* decode leading byte */ 2115 if (c < 0xC2) { 2116 skip = 1; goto invalid_utf8_sequence; 2117 } else if (c <= 0xDF) { 2118 cont = 1; n = c & 0x1f; 2119 } else if (c <= 0xEF) { 2120 cont = 2; n = c & 0xf; 2121 } else if (c <= 0xF4) { 2122 cont = 3; n = c & 0x7; 2123 } else { 2124 skip = 1; goto invalid_utf8_sequence; 2125 } 2126 2127 /* decode continuation bytes */ 2128 for (i = 1; i <= cont; i++) { 2129 int l = 0x80, h = 0xBF; 2130 2131 /* adjust limit for second byte */ 2132 if (i == 1) { 2133 switch (c) { 2134 case 0xE0: l = 0xA0; break; 2135 case 0xED: h = 0x9F; break; 2136 case 0xF0: l = 0x90; break; 2137 case 0xF4: h = 0x8F; break; 2138 } 2139 } 2140 2141 if (p[i] < l || p[i] > h) { 2142 skip = i; goto invalid_utf8_sequence; 2143 } 2144 2145 n = (n << 6) | (p[i] & 0x3f); 2146 } 2147 2148 /* advance pointer */ 2149 p += 1 + cont; 2150 c = n; 2151 goto add_char_nonext; 2152 2153 /* error handling */ 2154 invalid_utf8_sequence: 2155 tcc_warning("ill-formed UTF-8 subsequence starting with: \'\\x%x\'", c); 2156 c = 0xFFFD; 2157 p += skip; 2158 goto add_char_nonext; 2159 1733 2160 } 1734 2161 p++; … … 1736 2163 if (!is_long) 1737 2164 cstr_ccat(outstr, c); 1738 else 2165 else { 2166 #ifdef TCC_TARGET_PE 2167 /* store as UTF-16 */ 2168 if (c < 0x10000) { 2169 cstr_wccat(outstr, c); 2170 } else { 2171 c -= 0x10000; 2172 cstr_wccat(outstr, (c >> 10) + 0xD800); 2173 cstr_wccat(outstr, (c & 0x3FF) + 0xDC00); 2174 } 2175 #else 1739 2176 cstr_wccat(outstr, c); 2177 #endif 2178 } 1740 2179 } 1741 2180 /* add a trailing '\0' */ … … 1744 2183 else 1745 2184 cstr_wccat(outstr, '\0'); 2185 } 2186 2187 static void parse_string(const char *s, int len) 2188 { 2189 uint8_t buf[1000], *p = buf; 2190 int is_long, sep; 2191 2192 if ((is_long = *s == 'L')) 2193 ++s, --len; 2194 sep = *s++; 2195 len -= 2; 2196 if (len >= sizeof buf) 2197 p = tcc_malloc(len + 1); 2198 memcpy(p, s, len); 2199 p[len] = 0; 2200 2201 cstr_reset(&tokcstr); 2202 parse_escape_string(&tokcstr, p, is_long); 2203 if (p != buf) 2204 tcc_free(p); 2205 2206 if (sep == '\'') { 2207 int char_size, i, n, c; 2208 /* XXX: make it portable */ 2209 if (!is_long) 2210 tok = TOK_CCHAR, char_size = 1; 2211 else 2212 tok = TOK_LCHAR, char_size = sizeof(nwchar_t); 2213 n = tokcstr.size / char_size - 1; 2214 if (n < 1) 2215 tcc_error("empty character constant"); 2216 if (n > 1) 2217 tcc_warning("multi-character character constant"); 2218 for (c = i = 0; i < n; ++i) { 2219 if (is_long) 2220 c = ((nwchar_t *)tokcstr.data)[i]; 2221 else 2222 c = (c << 8) | ((char *)tokcstr.data)[i]; 2223 } 2224 tokc.i = c; 2225 } else { 2226 tokc.str.size = tokcstr.size; 2227 tokc.str.data = tokcstr.data; 2228 if (!is_long) 2229 tok = TOK_STR; 2230 else 2231 tok = TOK_LSTR; 2232 } 1746 2233 } 1747 2234 … … 1831 2318 shift = 4; 1832 2319 else 1833 shift = 2;2320 shift = 1; 1834 2321 bn_zero(bn); 1835 2322 q = token_buf; … … 1969 2456 } else { 1970 2457 unsigned long long n, n1; 1971 int lcount, ucount; 2458 int lcount, ucount, ov = 0; 2459 const char *p1; 1972 2460 1973 2461 /* integer number */ … … 1982 2470 t = *q++; 1983 2471 /* no need for checks except for base 10 / 8 errors */ 1984 if (t == '\0') {2472 if (t == '\0') 1985 2473 break; 1986 } else if (t >= 'a') {2474 else if (t >= 'a') 1987 2475 t = t - 'a' + 10; 1988 } else if (t >= 'A') {2476 else if (t >= 'A') 1989 2477 t = t - 'A' + 10; 1990 } else {2478 else 1991 2479 t = t - '0'; 1992 if (t >= b) 1993 tcc_error("invalid digit"); 1994 } 2480 if (t >= b) 2481 tcc_error("invalid digit"); 1995 2482 n1 = n; 1996 2483 n = n * b + t; 1997 2484 /* detect overflow */ 1998 /* XXX: this test is not reliable */ 1999 if (n < n1) 2000 tcc_error("integer constant overflow"); 2001 } 2002 2003 /* XXX: not exactly ANSI compliant */ 2004 if ((n & 0xffffffff00000000LL) != 0) { 2005 if ((n >> 63) != 0) 2006 tok = TOK_CULLONG; 2007 else 2008 tok = TOK_CLLONG; 2009 } else if (n > 0x7fffffff) { 2010 tok = TOK_CUINT; 2011 } else { 2012 tok = TOK_CINT; 2013 } 2014 lcount = 0; 2015 ucount = 0; 2485 if (n1 >= 0x1000000000000000ULL && n / b != n1) 2486 ov = 1; 2487 } 2488 2489 /* Determine the characteristics (unsigned and/or 64bit) the type of 2490 the constant must have according to the constant suffix(es) */ 2491 lcount = ucount = 0; 2492 p1 = p; 2016 2493 for(;;) { 2017 2494 t = toup(ch); … … 2019 2496 if (lcount >= 2) 2020 2497 tcc_error("three 'l's in integer constant"); 2498 if (lcount && *(p - 1) != ch) 2499 tcc_error("incorrect integer suffix: %s", p1); 2021 2500 lcount++; 2022 #if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE2023 if (lcount == 2) {2024 #endif2025 if (tok == TOK_CINT)2026 tok = TOK_CLLONG;2027 else if (tok == TOK_CUINT)2028 tok = TOK_CULLONG;2029 #if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE2030 }2031 #endif2032 2501 ch = *p++; 2033 2502 } else if (t == 'U') { … … 2035 2504 tcc_error("two 'u's in integer constant"); 2036 2505 ucount++; 2037 if (tok == TOK_CINT)2038 tok = TOK_CUINT;2039 else if (tok == TOK_CLLONG)2040 tok = TOK_CULLONG;2041 2506 ch = *p++; 2042 2507 } else { … … 2044 2509 } 2045 2510 } 2046 if (tok == TOK_CINT || tok == TOK_CUINT) 2047 tokc.ui = n; 2048 else 2049 tokc.ull = n; 2511 2512 /* Determine if it needs 64 bits and/or unsigned in order to fit */ 2513 if (ucount == 0 && b == 10) { 2514 if (lcount <= (LONG_SIZE == 4)) { 2515 if (n >= 0x80000000U) 2516 lcount = (LONG_SIZE == 4) + 1; 2517 } 2518 if (n >= 0x8000000000000000ULL) 2519 ov = 1, ucount = 1; 2520 } else { 2521 if (lcount <= (LONG_SIZE == 4)) { 2522 if (n >= 0x100000000ULL) 2523 lcount = (LONG_SIZE == 4) + 1; 2524 else if (n >= 0x80000000U) 2525 ucount = 1; 2526 } 2527 if (n >= 0x8000000000000000ULL) 2528 ucount = 1; 2529 } 2530 2531 if (ov) 2532 tcc_warning("integer constant overflow"); 2533 2534 tok = TOK_CINT; 2535 if (lcount) { 2536 tok = TOK_CLONG; 2537 if (lcount == 2) 2538 tok = TOK_CLLONG; 2539 } 2540 if (ucount) 2541 ++tok; /* TOK_CU... */ 2542 tokc.i = n; 2050 2543 } 2051 2544 if (ch) … … 2068 2561 static inline void next_nomacro1(void) 2069 2562 { 2070 int t, c, is_long ;2563 int t, c, is_long, len; 2071 2564 TokenSym *ts; 2072 2565 uint8_t *p, *p1; … … 2081 2574 tok = c; 2082 2575 p++; 2083 goto keep_tok_flags; 2576 if (parse_flags & PARSE_FLAG_SPACES) 2577 goto keep_tok_flags; 2578 while (isidnum_table[*p - CH_EOF] & IS_SPC) 2579 ++p; 2580 goto redo_no_start; 2084 2581 case '\f': 2085 2582 case '\v': … … 2089 2586 case '\\': 2090 2587 /* first look if it is in fact an end of buffer */ 2091 if (p >= file->buf_end) { 2092 file->buf_ptr = p; 2093 handle_eob(); 2094 p = file->buf_ptr; 2095 if (p >= file->buf_end) 2096 goto parse_eof; 2097 else 2098 goto redo_no_start; 2099 } else { 2100 file->buf_ptr = p; 2101 ch = *p; 2102 handle_stray(); 2103 p = file->buf_ptr; 2588 c = handle_stray1(p); 2589 p = file->buf_ptr; 2590 if (c == '\\') 2591 goto parse_simple; 2592 if (c != CH_EOF) 2104 2593 goto redo_no_start; 2105 }2106 parse_eof:2107 2594 { 2108 2595 TCCState *s1 = tcc_state; … … 2129 2616 printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL)); 2130 2617 #endif 2131 add_cached_include(s1, file->filename, file->ifndef_macro_saved); 2618 search_cached_include(s1, file->filename, 1) 2619 ->ifndef_macro = file->ifndef_macro_saved; 2132 2620 tok_flags &= ~TOK_FLAG_ENDIF; 2133 2621 } … … 2141 2629 s1->include_stack_ptr--; 2142 2630 p = file->buf_ptr; 2631 if (p == file->buffer) 2632 tok_flags = TOK_FLAG_BOF|TOK_FLAG_BOL; 2143 2633 goto redo_no_start; 2144 2634 } … … 2170 2660 tok = TOK_TWOSHARPS; 2171 2661 } else { 2172 if (parse_flags & PARSE_FLAG_ASM_ COMMENTS) {2662 if (parse_flags & PARSE_FLAG_ASM_FILE) { 2173 2663 p = parse_line_comment(p - 1); 2174 2664 goto redo_no_start; … … 2179 2669 } 2180 2670 break; 2671 2672 /* dollar is allowed to start identifiers when not parsing asm */ 2673 case '$': 2674 if (!(isidnum_table[c - CH_EOF] & IS_ID) 2675 || (parse_flags & PARSE_FLAG_ASM_FILE)) 2676 goto parse_simple; 2181 2677 2182 2678 case 'a': case 'b': case 'c': case 'd': … … 2199 2695 h = TOK_HASH_INIT; 2200 2696 h = TOK_HASH_FUNC(h, c); 2201 p++; 2202 for(;;) { 2203 c = *p; 2204 if (!isidnum_table[c-CH_EOF]) 2205 break; 2697 while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) 2206 2698 h = TOK_HASH_FUNC(h, c); 2207 p++; 2208 } 2699 len = p - p1; 2209 2700 if (c != '\\') { 2210 2701 TokenSym **pts; 2211 int len;2212 2702 2213 2703 /* fast case : no stray found, so we have the full token 2214 2704 and we have already hashed it */ 2215 len = p - p1;2216 2705 h &= (TOK_HASH_SIZE - 1); 2217 2706 pts = &hash_ident[h]; … … 2224 2713 pts = &(ts->hash_next); 2225 2714 } 2226 ts = tok_alloc_new(pts, p1, len);2715 ts = tok_alloc_new(pts, (char *) p1, len); 2227 2716 token_found: ; 2228 2717 } else { 2229 2718 /* slower case */ 2230 2719 cstr_reset(&tokcstr); 2231 2232 while (p1 < p) { 2233 cstr_ccat(&tokcstr, *p1); 2234 p1++; 2235 } 2720 cstr_cat(&tokcstr, (char *) p1, len); 2236 2721 p--; 2237 2722 PEEKC(c, p); 2238 2723 parse_ident_slow: 2239 while (isidnum_table[c-CH_EOF]) { 2724 while (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) 2725 { 2240 2726 cstr_ccat(&tokcstr, c); 2241 2727 PEEKC(c, p); … … 2262 2748 } 2263 2749 break; 2750 2264 2751 case '0': case '1': case '2': case '3': 2265 2752 case '4': case '5': case '6': case '7': 2266 2753 case '8': case '9': 2267 2268 cstr_reset(&tokcstr);2754 t = c; 2755 PEEKC(c, p); 2269 2756 /* after the first digit, accept digits, alpha, '.' or sign if 2270 2757 prefixed by 'eEpP' */ 2271 2758 parse_num: 2759 cstr_reset(&tokcstr); 2272 2760 for(;;) { 2761 cstr_ccat(&tokcstr, t); 2762 if (!((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) 2763 || c == '.' 2764 || ((c == '+' || c == '-') 2765 && (((t == 'e' || t == 'E') 2766 && !(parse_flags & PARSE_FLAG_ASM_FILE 2767 /* 0xe+1 is 3 tokens in asm */ 2768 && ((char*)tokcstr.data)[0] == '0' 2769 && toup(((char*)tokcstr.data)[1]) == 'X')) 2770 || t == 'p' || t == 'P')))) 2771 break; 2273 2772 t = c; 2274 cstr_ccat(&tokcstr, c);2275 2773 PEEKC(c, p); 2276 if (!(isnum(c) || isid(c) || c == '.' ||2277 ((c == '+' || c == '-') &&2278 (t == 'e' || t == 'E' || t == 'p' || t == 'P'))))2279 break;2280 2774 } 2281 2775 /* We add a trailing '\0' to ease parsing */ 2282 2776 cstr_ccat(&tokcstr, '\0'); 2283 tokc.cstr = &tokcstr; 2777 tokc.str.size = tokcstr.size; 2778 tokc.str.data = tokcstr.data; 2284 2779 tok = TOK_PPNUM; 2285 2780 break; 2781 2286 2782 case '.': 2287 2783 /* special dot handling because it can also start a number */ 2288 2784 PEEKC(c, p); 2289 2785 if (isnum(c)) { 2290 cstr_reset(&tokcstr); 2291 cstr_ccat(&tokcstr, '.'); 2786 t = '.'; 2292 2787 goto parse_num; 2788 } else if ((isidnum_table['.' - CH_EOF] & IS_ID) 2789 && (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))) { 2790 *--p = c = '.'; 2791 goto parse_ident_fast; 2293 2792 } else if (c == '.') { 2294 2793 PEEKC(c, p); 2295 if (c != '.') 2296 expect("'.'"); 2297 PEEKC(c, p); 2298 tok = TOK_DOTS; 2794 if (c == '.') { 2795 p++; 2796 tok = TOK_DOTS; 2797 } else { 2798 *--p = '.'; /* may underflow into file->unget[] */ 2799 tok = '.'; 2800 } 2299 2801 } else { 2300 2802 tok = '.'; … … 2305 2807 is_long = 0; 2306 2808 str_const: 2307 { 2308 CString str; 2309 int sep; 2310 2311 sep = c; 2312 2313 /* parse the string */ 2314 cstr_new(&str); 2315 p = parse_pp_string(p, sep, &str); 2316 cstr_ccat(&str, '\0'); 2317 2318 /* eval the escape (should be done as TOK_PPNUM) */ 2319 cstr_reset(&tokcstr); 2320 parse_escape_string(&tokcstr, str.data, is_long); 2321 cstr_free(&str); 2322 2323 if (sep == '\'') { 2324 int char_size; 2325 /* XXX: make it portable */ 2326 if (!is_long) 2327 char_size = 1; 2328 else 2329 char_size = sizeof(nwchar_t); 2330 if (tokcstr.size <= char_size) 2331 tcc_error("empty character constant"); 2332 if (tokcstr.size > 2 * char_size) 2333 tcc_warning("multi-character character constant"); 2334 if (!is_long) { 2335 tokc.i = *(int8_t *)tokcstr.data; 2336 tok = TOK_CCHAR; 2337 } else { 2338 tokc.i = *(nwchar_t *)tokcstr.data; 2339 tok = TOK_LCHAR; 2340 } 2341 } else { 2342 tokc.cstr = &tokcstr; 2343 if (!is_long) 2344 tok = TOK_STR; 2345 else 2346 tok = TOK_LSTR; 2347 } 2348 } 2809 cstr_reset(&tokcstr); 2810 if (is_long) 2811 cstr_ccat(&tokcstr, 'L'); 2812 cstr_ccat(&tokcstr, c); 2813 p = parse_pp_string(p, c, &tokcstr); 2814 cstr_ccat(&tokcstr, c); 2815 cstr_ccat(&tokcstr, '\0'); 2816 tokc.str.size = tokcstr.size; 2817 tokc.str.data = tokcstr.data; 2818 tok = TOK_PPSTR; 2349 2819 break; 2350 2820 … … 2366 2836 } 2367 2837 break; 2368 2369 2838 case '>': 2370 2839 PEEKC(c, p); … … 2478 2947 case '?': 2479 2948 case '~': 2480 case ' $': /* only used in assembler */2481 case '@': /* dito */2949 case '@': /* only used in assembler */ 2950 parse_simple: 2482 2951 tok = c; 2483 2952 p++; 2484 2953 break; 2485 2954 default: 2955 if (c >= 0x80 && c <= 0xFF) /* utf8 identifiers */ 2956 goto parse_ident_fast; 2957 if (parse_flags & PARSE_FLAG_ASM_FILE) 2958 goto parse_simple; 2486 2959 tcc_error("unrecognized character \\x%02x", c); 2487 2960 break; … … 2491 2964 file->buf_ptr = p; 2492 2965 #if defined(PARSE_DEBUG) 2493 printf("token = % s\n", get_tok_str(tok, &tokc));2966 printf("token = %d %s\n", tok, get_tok_str(tok, &tokc)); 2494 2967 #endif 2495 2968 } … … 2512 2985 next_nomacro1(); 2513 2986 } 2987 //printf("token = %s\n", get_tok_str(tok, &tokc)); 2514 2988 } 2515 2989 … … 2518 2992 do { 2519 2993 next_nomacro_spc(); 2520 } while ( is_space(tok));2994 } while (tok < 256 && (isidnum_table[tok - CH_EOF] & IS_SPC)); 2521 2995 } 2522 2996 2523 /* substitute args in macro_str and return allocated string */ 2997 2998 static void macro_subst( 2999 TokenString *tok_str, 3000 Sym **nested_list, 3001 const int *macro_str 3002 ); 3003 3004 /* substitute arguments in replacement lists in macro_str by the values in 3005 args (field d) and return allocated string */ 2524 3006 static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) 2525 3007 { 2526 int last_tok, t, spc;3008 int t, t0, t1, spc; 2527 3009 const int *st; 2528 3010 Sym *s; … … 2532 3014 2533 3015 tok_str_new(&str); 2534 last_tok= 0;3016 t0 = t1 = 0; 2535 3017 while(1) { 2536 3018 TOK_GET(&t, ¯o_str, &cval); … … 2541 3023 TOK_GET(&t, ¯o_str, &cval); 2542 3024 if (!t) 2543 break;3025 goto bad_stringy; 2544 3026 s = sym_find2(args, t); 2545 3027 if (s) { 2546 3028 cstr_new(&cstr); 3029 cstr_ccat(&cstr, '\"'); 2547 3030 st = s->d; 2548 3031 spc = 0; 2549 while (*st ) {3032 while (*st >= 0) { 2550 3033 TOK_GET(&t, &st, &cval); 2551 if (!check_space(t, &spc)) 2552 cstr_cat(&cstr, get_tok_str(t, &cval)); 3034 if (t != TOK_PLCHLDR 3035 && t != TOK_NOSUBST 3036 && 0 == check_space(t, &spc)) { 3037 const char *s = get_tok_str(t, &cval); 3038 while (*s) { 3039 if (t == TOK_PPSTR && *s != '\'') 3040 add_char(&cstr, *s); 3041 else 3042 cstr_ccat(&cstr, *s); 3043 ++s; 3044 } 3045 } 2553 3046 } 2554 3047 cstr.size -= spc; 3048 cstr_ccat(&cstr, '\"'); 2555 3049 cstr_ccat(&cstr, '\0'); 2556 3050 #ifdef PP_DEBUG 2557 printf(" stringize: %s\n", (char *)cstr.data);3051 printf("\nstringize: <%s>\n", (char *)cstr.data); 2558 3052 #endif 2559 3053 /* add string */ 2560 cval.cstr = &cstr; 2561 tok_str_add2(&str, TOK_STR, &cval); 3054 cval.str.size = cstr.size; 3055 cval.str.data = cstr.data; 3056 tok_str_add2(&str, TOK_PPSTR, &cval); 2562 3057 cstr_free(&cstr); 2563 3058 } else { 2564 tok_str_add2(&str, t, &cval); 3059 bad_stringy: 3060 expect("macro parameter after '#'"); 2565 3061 } 2566 3062 } else if (t >= TOK_IDENT) { 2567 3063 s = sym_find2(args, t); 2568 3064 if (s) { 3065 int l0 = str.len; 2569 3066 st = s->d; 2570 3067 /* if '##' is present before or after, no arg substitution */ 2571 if (*macro_str == TOK_TWOSHARPS || last_tok == TOK_TWOSHARPS) { 2572 /* special case for var arg macros : ## eats the 2573 ',' if empty VA_ARGS variable. */ 2574 /* XXX: test of the ',' is not 100% 2575 reliable. should fix it to avoid security 2576 problems */ 2577 if (gnu_ext && s->type.t && 2578 last_tok == TOK_TWOSHARPS && 2579 str.len >= 2 && str.str[str.len - 2] == ',') { 2580 if (*st == 0) { 3068 if (*macro_str == TOK_PPJOIN || t1 == TOK_PPJOIN) { 3069 /* special case for var arg macros : ## eats the ',' 3070 if empty VA_ARGS variable. */ 3071 if (t1 == TOK_PPJOIN && t0 == ',' && gnu_ext && s->type.t) { 3072 if (*st <= 0) { 2581 3073 /* suppress ',' '##' */ 2582 3074 str.len -= 2; … … 2586 3078 goto add_var; 2587 3079 } 2588 } else {2589 int t1;2590 add_var:2591 for(;;) {2592 TOK_GET(&t1, &st, &cval);2593 if (!t1)2594 break;2595 tok_str_add2(&str, t1, &cval);2596 }2597 3080 } 2598 3081 } else { 2599 /* NOTE: the stream cannot be read when macro 2600 substituing an argument */ 2601 macro_subst(&str, nested_list, st, NULL); 3082 add_var: 3083 if (!s->next) { 3084 /* Expand arguments tokens and store them. In most 3085 cases we could also re-expand each argument if 3086 used multiple times, but not if the argument 3087 contains the __COUNTER__ macro. */ 3088 TokenString str2; 3089 sym_push2(&s->next, s->v, s->type.t, 0); 3090 tok_str_new(&str2); 3091 macro_subst(&str2, nested_list, st); 3092 tok_str_add(&str2, 0); 3093 s->next->d = str2.str; 3094 } 3095 st = s->next->d; 2602 3096 } 3097 for(;;) { 3098 int t2; 3099 TOK_GET(&t2, &st, &cval); 3100 if (t2 <= 0) 3101 break; 3102 tok_str_add2(&str, t2, &cval); 3103 } 3104 if (str.len == l0) /* expanded to empty string */ 3105 tok_str_add(&str, TOK_PLCHLDR); 2603 3106 } else { 2604 3107 tok_str_add(&str, t); … … 2607 3110 tok_str_add2(&str, t, &cval); 2608 3111 } 2609 last_tok= t;3112 t0 = t1, t1 = t; 2610 3113 } 2611 3114 tok_str_add(&str, 0); … … 2618 3121 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 2619 3122 }; 3123 3124 static int paste_tokens(int t1, CValue *v1, int t2, CValue *v2) 3125 { 3126 CString cstr; 3127 int n, ret = 1; 3128 3129 cstr_new(&cstr); 3130 if (t1 != TOK_PLCHLDR) 3131 cstr_cat(&cstr, get_tok_str(t1, v1), -1); 3132 n = cstr.size; 3133 if (t2 != TOK_PLCHLDR) 3134 cstr_cat(&cstr, get_tok_str(t2, v2), -1); 3135 cstr_ccat(&cstr, '\0'); 3136 3137 tcc_open_bf(tcc_state, ":paste:", cstr.size); 3138 memcpy(file->buffer, cstr.data, cstr.size); 3139 tok_flags = 0; 3140 for (;;) { 3141 next_nomacro1(); 3142 if (0 == *file->buf_ptr) 3143 break; 3144 if (is_space(tok)) 3145 continue; 3146 tcc_warning("pasting \"%.*s\" and \"%s\" does not give a valid" 3147 " preprocessing token", n, cstr.data, (char*)cstr.data + n); 3148 ret = 0; 3149 break; 3150 } 3151 tcc_close(); 3152 //printf("paste <%s>\n", (char*)cstr.data); 3153 cstr_free(&cstr); 3154 return ret; 3155 } 3156 3157 /* handle the '##' operator. Return NULL if no '##' seen. Otherwise 3158 return the resulting string (which must be freed). */ 3159 static inline int *macro_twosharps(const int *ptr0) 3160 { 3161 int t; 3162 CValue cval; 3163 TokenString macro_str1; 3164 int start_of_nosubsts = -1; 3165 const int *ptr; 3166 3167 /* we search the first '##' */ 3168 for (ptr = ptr0;;) { 3169 TOK_GET(&t, &ptr, &cval); 3170 if (t == TOK_PPJOIN) 3171 break; 3172 if (t == 0) 3173 return NULL; 3174 } 3175 3176 tok_str_new(¯o_str1); 3177 3178 //tok_print(" $$$", ptr0); 3179 for (ptr = ptr0;;) { 3180 TOK_GET(&t, &ptr, &cval); 3181 if (t == 0) 3182 break; 3183 if (t == TOK_PPJOIN) 3184 continue; 3185 while (*ptr == TOK_PPJOIN) { 3186 int t1; CValue cv1; 3187 /* given 'a##b', remove nosubsts preceding 'a' */ 3188 if (start_of_nosubsts >= 0) 3189 macro_str1.len = start_of_nosubsts; 3190 /* given 'a##b', remove nosubsts preceding 'b' */ 3191 while ((t1 = *++ptr) == TOK_NOSUBST) 3192 ; 3193 if (t1 && t1 != TOK_PPJOIN) { 3194 TOK_GET(&t1, &ptr, &cv1); 3195 if (t != TOK_PLCHLDR || t1 != TOK_PLCHLDR) { 3196 if (paste_tokens(t, &cval, t1, &cv1)) { 3197 t = tok, cval = tokc; 3198 } else { 3199 tok_str_add2(¯o_str1, t, &cval); 3200 t = t1, cval = cv1; 3201 } 3202 } 3203 } 3204 } 3205 if (t == TOK_NOSUBST) { 3206 if (start_of_nosubsts < 0) 3207 start_of_nosubsts = macro_str1.len; 3208 } else { 3209 start_of_nosubsts = -1; 3210 } 3211 tok_str_add2(¯o_str1, t, &cval); 3212 } 3213 tok_str_add(¯o_str1, 0); 3214 //tok_print(" ###", macro_str1.str); 3215 return macro_str1.str; 3216 } 3217 3218 /* peek or read [ws_str == NULL] next token from function macro call, 3219 walking up macro levels up to the file if necessary */ 3220 static int next_argstream(Sym **nested_list, TokenString *ws_str) 3221 { 3222 int t; 3223 const int *p; 3224 Sym *sa; 3225 3226 for (;;) { 3227 if (macro_ptr) { 3228 p = macro_ptr, t = *p; 3229 if (ws_str) { 3230 while (is_space(t) || TOK_LINEFEED == t || TOK_PLCHLDR == t) 3231 tok_str_add(ws_str, t), t = *++p; 3232 } 3233 if (t == 0) { 3234 end_macro(); 3235 /* also, end of scope for nested defined symbol */ 3236 sa = *nested_list; 3237 while (sa && sa->v == 0) 3238 sa = sa->prev; 3239 if (sa) 3240 sa->v = 0; 3241 continue; 3242 } 3243 } else { 3244 ch = handle_eob(); 3245 if (ws_str) { 3246 while (is_space(ch) || ch == '\n' || ch == '/') { 3247 if (ch == '/') { 3248 int c; 3249 uint8_t *p = file->buf_ptr; 3250 PEEKC(c, p); 3251 if (c == '*') { 3252 p = parse_comment(p); 3253 file->buf_ptr = p - 1; 3254 } else if (c == '/') { 3255 p = parse_line_comment(p); 3256 file->buf_ptr = p - 1; 3257 } else 3258 break; 3259 ch = ' '; 3260 } 3261 if (ch == '\n') 3262 file->line_num++; 3263 if (!(ch == '\f' || ch == '\v' || ch == '\r')) 3264 tok_str_add(ws_str, ch); 3265 cinp(); 3266 } 3267 } 3268 t = ch; 3269 } 3270 3271 if (ws_str) 3272 return t; 3273 next_nomacro_spc(); 3274 return tok; 3275 } 3276 } 2620 3277 2621 3278 /* do macro substitution of current token with macro 's' and add … … 2623 3280 macros we got inside to avoid recursing. Return non zero if no 2624 3281 substitution needs to be done */ 2625 static int macro_subst_tok(TokenString *tok_str, 2626 Sym **nested_list, Sym *s, struct macro_level **can_read_stream) 3282 static int macro_subst_tok( 3283 TokenString *tok_str, 3284 Sym **nested_list, 3285 Sym *s) 2627 3286 { 2628 3287 Sym *args, *sa, *sa1; 2629 int mstr_allocated, parlevel, *mstr, t, t1, spc; 2630 const int *p; 3288 int parlevel, t, t1, spc; 2631 3289 TokenString str; 2632 3290 char *cstrval; … … 2634 3292 CString cstr; 2635 3293 char buf[32]; 2636 3294 2637 3295 /* if symbol is a macro, prepare substitution */ 2638 3296 /* special macros */ 2639 if (tok == TOK___LINE__) { 2640 snprintf(buf, sizeof(buf), "%d", file->line_num); 3297 if (tok == TOK___LINE__ || tok == TOK___COUNTER__) { 3298 t = tok == TOK___LINE__ ? file->line_num : pp_counter++; 3299 snprintf(buf, sizeof(buf), "%d", t); 2641 3300 cstrval = buf; 2642 3301 t1 = TOK_PPNUM; … … 2663 3322 add_cstr1: 2664 3323 cstr_new(&cstr); 2665 cstr_cat(&cstr, cstrval );2666 c str_ccat(&cstr, '\0');2667 cval. cstr = &cstr;3324 cstr_cat(&cstr, cstrval, 0); 3325 cval.str.size = cstr.size; 3326 cval.str.data = cstr.data; 2668 3327 tok_str_add2(tok_str, t1, &cval); 2669 3328 cstr_free(&cstr); 2670 } else { 2671 mstr = s->d; 2672 mstr_allocated = 0; 3329 } else if (s->d) { 3330 int saved_parse_flags = parse_flags; 3331 int *joined_str = NULL; 3332 int *mstr = s->d; 3333 2673 3334 if (s->type.t == MACRO_FUNC) { 2674 /* NOTE: we do not use next_nomacro to avoid eating the2675 next token. XXX: find better solution */2676 redo:2677 if (macro_ptr) { 2678 p = macro_ptr;2679 while (is_space(t = *p) || TOK_LINEFEED == t)2680 ++p;2681 if (t == 0 && can_read_stream) { 2682 /* end of macro stream: we must look at the token2683 after in the file */2684 struct macro_level *ml = *can_read_stream;2685 macro_ptr = NULL;2686 if (ml)2687 {2688 macro_ptr = ml->p;2689 ml->p = NULL;2690 *can_read_stream = ml -> prev;2691 }2692 /* also, end of scope for nested defined symbol */2693 (*nested_list)->v = -1;2694 goto redo;3335 /* whitespace between macro name and argument list */ 3336 TokenString ws_str; 3337 tok_str_new(&ws_str); 3338 3339 spc = 0; 3340 parse_flags |= PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED 3341 | PARSE_FLAG_ACCEPT_STRAYS; 3342 3343 /* get next token from argument stream */ 3344 t = next_argstream(nested_list, &ws_str); 3345 if (t != '(') { 3346 /* not a macro substitution after all, restore the 3347 * macro token plus all whitespace we've read. 3348 * whitespace is intentionally not merged to preserve 3349 * newlines. */ 3350 parse_flags = saved_parse_flags; 3351 tok_str_add(tok_str, tok); 3352 if (parse_flags & PARSE_FLAG_SPACES) { 3353 int i; 3354 for (i = 0; i < ws_str.len; i++) 3355 tok_str_add(tok_str, ws_str.str[i]); 2695 3356 } 3357 tok_str_free_str(ws_str.str); 3358 return 0; 2696 3359 } else { 2697 ch = file->buf_ptr[0]; 2698 while (is_space(ch) || ch == '\n' || ch == '/') 2699 { 2700 if (ch == '/') 2701 { 2702 int c; 2703 uint8_t *p = file->buf_ptr; 2704 PEEKC(c, p); 2705 if (c == '*') { 2706 p = parse_comment(p); 2707 file->buf_ptr = p - 1; 2708 } else if (c == '/') { 2709 p = parse_line_comment(p); 2710 file->buf_ptr = p - 1; 2711 } else 2712 break; 2713 } 2714 cinp(); 2715 } 2716 t = ch; 2717 } 2718 if (t != '(') /* no macro subst */ 2719 return -1; 2720 3360 tok_str_free_str(ws_str.str); 3361 } 3362 do { 3363 next_nomacro(); /* eat '(' */ 3364 } while (tok == TOK_PLCHLDR); 3365 2721 3366 /* argument macro */ 2722 next_nomacro();2723 next_nomacro();2724 3367 args = NULL; 2725 3368 sa = s->next; 2726 3369 /* NOTE: empty args are allowed, except if no args */ 2727 3370 for(;;) { 3371 do { 3372 next_argstream(nested_list, NULL); 3373 } while (is_space(tok) || TOK_LINEFEED == tok); 3374 empty_arg: 2728 3375 /* handle '()' case */ 2729 3376 if (!args && !sa && tok == ')') … … 2737 3384 while ((parlevel > 0 || 2738 3385 (tok != ')' && 2739 (tok != ',' || sa->type.t))) && 2740 tok != -1) { 3386 (tok != ',' || sa->type.t)))) { 3387 if (tok == TOK_EOF || tok == 0) 3388 break; 2741 3389 if (tok == '(') 2742 3390 parlevel++; … … 2747 3395 if (!check_space(tok, &spc)) 2748 3396 tok_str_add2(&str, tok, &tokc); 2749 next_ nomacro_spc();3397 next_argstream(nested_list, NULL); 2750 3398 } 3399 if (parlevel) 3400 expect(")"); 2751 3401 str.len -= spc; 3402 tok_str_add(&str, -1); 2752 3403 tok_str_add(&str, 0); 2753 3404 sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0); … … 2758 3409 var arg argument if it is omitted */ 2759 3410 if (sa && sa->type.t && gnu_ext) 2760 continue; 2761 else 2762 break; 3411 goto empty_arg; 3412 break; 2763 3413 } 2764 3414 if (tok != ',') 2765 3415 expect(","); 2766 next_nomacro();2767 3416 } 2768 3417 if (sa) { … … 2770 3419 get_tok_str(s->v, 0)); 2771 3420 } 3421 3422 parse_flags = saved_parse_flags; 2772 3423 2773 3424 /* now subst each arg */ … … 2777 3428 while (sa) { 2778 3429 sa1 = sa->prev; 2779 tok_str_free(sa->d); 3430 tok_str_free_str(sa->d); 3431 if (sa->next) { 3432 tok_str_free_str(sa->next->d); 3433 sym_free(sa->next); 3434 } 2780 3435 sym_free(sa); 2781 3436 sa = sa1; 2782 3437 } 2783 mstr_allocated = 1;2784 } 3438 } 3439 2785 3440 sym_push2(nested_list, s->v, 0, 0); 2786 macro_subst(tok_str, nested_list, mstr, can_read_stream); 3441 parse_flags = saved_parse_flags; 3442 joined_str = macro_twosharps(mstr); 3443 macro_subst(tok_str, nested_list, joined_str ? joined_str : mstr); 3444 2787 3445 /* pop nested defined symbol */ 2788 3446 sa1 = *nested_list; 2789 3447 *nested_list = sa1->prev; 2790 3448 sym_free(sa1); 2791 if (mstr_allocated) 2792 tok_str_free(mstr); 3449 if (joined_str) 3450 tok_str_free_str(joined_str); 3451 if (mstr != s->d) 3452 tok_str_free_str(mstr); 2793 3453 } 2794 3454 return 0; 2795 3455 } 2796 2797 /* handle the '##' operator. Return NULL if no '##' seen. Otherwise2798 return the resulting string (which must be freed). */2799 static inline int *macro_twosharps(const int *macro_str)2800 {2801 const int *ptr;2802 int t;2803 TokenString macro_str1;2804 CString cstr;2805 int n, start_of_nosubsts;2806 2807 /* we search the first '##' */2808 for(ptr = macro_str;;) {2809 CValue cval;2810 TOK_GET(&t, &ptr, &cval);2811 if (t == TOK_TWOSHARPS)2812 break;2813 /* nothing more to do if end of string */2814 if (t == 0)2815 return NULL;2816 }2817 2818 /* we saw '##', so we need more processing to handle it */2819 start_of_nosubsts = -1;2820 tok_str_new(¯o_str1);2821 for(ptr = macro_str;;) {2822 TOK_GET(&tok, &ptr, &tokc);2823 if (tok == 0)2824 break;2825 if (tok == TOK_TWOSHARPS)2826 continue;2827 if (tok == TOK_NOSUBST && start_of_nosubsts < 0)2828 start_of_nosubsts = macro_str1.len;2829 while (*ptr == TOK_TWOSHARPS) {2830 /* given 'a##b', remove nosubsts preceding 'a' */2831 if (start_of_nosubsts >= 0)2832 macro_str1.len = start_of_nosubsts;2833 /* given 'a##b', skip '##' */2834 t = *++ptr;2835 /* given 'a##b', remove nosubsts preceding 'b' */2836 while (t == TOK_NOSUBST)2837 t = *++ptr;2838 if (t && t != TOK_TWOSHARPS) {2839 CValue cval;2840 TOK_GET(&t, &ptr, &cval);2841 /* We concatenate the two tokens */2842 cstr_new(&cstr);2843 cstr_cat(&cstr, get_tok_str(tok, &tokc));2844 n = cstr.size;2845 cstr_cat(&cstr, get_tok_str(t, &cval));2846 cstr_ccat(&cstr, '\0');2847 2848 tcc_open_bf(tcc_state, ":paste:", cstr.size);2849 memcpy(file->buffer, cstr.data, cstr.size);2850 for (;;) {2851 next_nomacro1();2852 if (0 == *file->buf_ptr)2853 break;2854 tok_str_add2(¯o_str1, tok, &tokc);2855 tcc_warning("pasting \"%.*s\" and \"%s\" does not give a valid preprocessing token",2856 n, cstr.data, (char*)cstr.data + n);2857 }2858 tcc_close();2859 cstr_free(&cstr);2860 }2861 }2862 if (tok != TOK_NOSUBST)2863 start_of_nosubsts = -1;2864 tok_str_add2(¯o_str1, tok, &tokc);2865 }2866 tok_str_add(¯o_str1, 0);2867 return macro_str1.str;2868 }2869 2870 3456 2871 3457 /* do macro substitution of macro_str and add result to 2872 3458 (tok_str,tok_len). 'nested_list' is the list of all macros we got 2873 3459 inside to avoid recursing. */ 2874 static void macro_subst(TokenString *tok_str, Sym **nested_list, 2875 const int *macro_str, struct macro_level ** can_read_stream) 3460 static void macro_subst( 3461 TokenString *tok_str, 3462 Sym **nested_list, 3463 const int *macro_str 3464 ) 2876 3465 { 2877 3466 Sym *s; 2878 int *macro_str1; 2879 const int *ptr; 2880 int t, ret, spc; 3467 int t, spc, nosubst; 2881 3468 CValue cval; 2882 struct macro_level ml;2883 int force_blank;2884 3469 2885 /* first scan for '##' operator handling */ 2886 ptr = macro_str; 2887 macro_str1 = macro_twosharps(ptr); 2888 2889 if (macro_str1) 2890 ptr = macro_str1; 2891 spc = 0; 2892 force_blank = 0; 3470 spc = nosubst = 0; 2893 3471 2894 3472 while (1) { 2895 /* NOTE: ptr == NULL can only happen if tokens are read from 2896 file stream due to a macro function call */ 2897 if (ptr == NULL) 3473 TOK_GET(&t, ¯o_str, &cval); 3474 if (t <= 0) 2898 3475 break; 2899 TOK_GET(&t, &ptr, &cval); 2900 if (t == 0) 2901 break; 2902 if (t == TOK_NOSUBST) { 2903 /* following token has already been subst'd. just copy it on */ 2904 tok_str_add2(tok_str, TOK_NOSUBST, NULL); 2905 TOK_GET(&t, &ptr, &cval); 2906 goto no_subst; 2907 } 2908 s = define_find(t); 2909 if (s != NULL) { 3476 3477 if (t >= TOK_IDENT && 0 == nosubst) { 3478 s = define_find(t); 3479 if (s == NULL) 3480 goto no_subst; 3481 2910 3482 /* if nested substitution, do nothing */ 2911 3483 if (sym_find2(*nested_list, t)) { … … 2914 3486 goto no_subst; 2915 3487 } 2916 ml.p = macro_ptr; 2917 if (can_read_stream) 2918 ml.prev = *can_read_stream, *can_read_stream = &ml; 2919 macro_ptr = (int *)ptr; 2920 tok = t; 2921 ret = macro_subst_tok(tok_str, nested_list, s, can_read_stream); 2922 ptr = (int *)macro_ptr; 2923 macro_ptr = ml.p; 2924 if (can_read_stream && *can_read_stream == &ml) 2925 *can_read_stream = ml.prev; 2926 if (ret != 0) 2927 goto no_subst; 2928 if (parse_flags & PARSE_FLAG_SPACES) 2929 force_blank = 1; 3488 3489 { 3490 TokenString str; 3491 str.str = (int*)macro_str; 3492 begin_macro(&str, 2); 3493 3494 tok = t; 3495 macro_subst_tok(tok_str, nested_list, s); 3496 3497 if (str.alloc == 3) { 3498 /* already finished by reading function macro arguments */ 3499 break; 3500 } 3501 3502 macro_str = macro_ptr; 3503 end_macro (); 3504 } 3505 if (tok_str->len) 3506 spc = is_space(t = tok_str->str[tok_str->lastlen]); 2930 3507 } else { 2931 no_subst: 2932 if (force_blank) { 2933 tok_str_add(tok_str, ' '); 2934 spc = 1; 2935 force_blank = 0; 2936 } 2937 if (!check_space(t, &spc)) 3508 if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS)) 3509 tcc_error("stray '\\' in program"); 3510 no_subst: 3511 if (!check_space(t, &spc)) 2938 3512 tok_str_add2(tok_str, t, &cval); 2939 } 2940 } 2941 if (macro_str1) 2942 tok_str_free(macro_str1); 3513 3514 if (nosubst) { 3515 if (nosubst > 1 && (spc || (++nosubst == 3 && t == '('))) 3516 continue; 3517 nosubst = 0; 3518 } 3519 if (t == TOK_NOSUBST) 3520 nosubst = 1; 3521 } 3522 /* GCC supports 'defined' as result of a macro substitution */ 3523 if (t == TOK_DEFINED && pp_expr) 3524 nosubst = 2; 3525 } 2943 3526 } 2944 3527 … … 2946 3529 ST_FUNC void next(void) 2947 3530 { 2948 Sym *nested_list, *s;2949 TokenString str;2950 struct macro_level *ml;2951 2952 3531 redo: 2953 3532 if (parse_flags & PARSE_FLAG_SPACES) … … 2955 3534 else 2956 3535 next_nomacro(); 2957 if (!macro_ptr) { 2958 /* if not reading from macro substituted string, then try 2959 to substitute macros */ 2960 if (tok >= TOK_IDENT && 2961 (parse_flags & PARSE_FLAG_PREPROCESS)) { 2962 s = define_find(tok); 2963 if (s) { 2964 /* we have a macro: we try to substitute */ 2965 tok_str_new(&str); 2966 nested_list = NULL; 2967 ml = NULL; 2968 if (macro_subst_tok(&str, &nested_list, s, &ml) == 0) { 2969 /* substitution done, NOTE: maybe empty */ 2970 tok_str_add(&str, 0); 2971 macro_ptr = str.str; 2972 macro_ptr_allocated = str.str; 2973 goto redo; 2974 } 2975 } 2976 } 2977 } else { 2978 if (tok == 0) { 2979 /* end of macro or end of unget buffer */ 2980 if (unget_buffer_enabled) { 2981 macro_ptr = unget_saved_macro_ptr; 2982 unget_buffer_enabled = 0; 2983 } else { 2984 /* end of macro string: free it */ 2985 tok_str_free(macro_ptr_allocated); 2986 macro_ptr_allocated = NULL; 2987 macro_ptr = NULL; 2988 } 3536 3537 if (macro_ptr) { 3538 if (tok == TOK_NOSUBST || tok == TOK_PLCHLDR) { 3539 /* discard preprocessor markers */ 2989 3540 goto redo; 2990 } else if (tok == TOK_NOSUBST) { 2991 /* discard preprocessor's nosubst markers */ 3541 } else if (tok == 0) { 3542 /* end of macro or unget token string */ 3543 end_macro(); 2992 3544 goto redo; 2993 3545 } 2994 } 2995 3546 } else if (tok >= TOK_IDENT && (parse_flags & PARSE_FLAG_PREPROCESS)) { 3547 Sym *s; 3548 /* if reading from file, try to substitute macros */ 3549 s = define_find(tok); 3550 if (s) { 3551 Sym *nested_list = NULL; 3552 tokstr_buf.len = 0; 3553 macro_subst_tok(&tokstr_buf, &nested_list, s); 3554 tok_str_add(&tokstr_buf, 0); 3555 begin_macro(&tokstr_buf, 2); 3556 goto redo; 3557 } 3558 } 2996 3559 /* convert preprocessor tokens into C tokens */ 2997 if (tok == TOK_PPNUM && 2998 (parse_flags & PARSE_FLAG_TOK_NUM)) { 2999 parse_number((char *)tokc.cstr->data); 3560 if (tok == TOK_PPNUM) { 3561 if (parse_flags & PARSE_FLAG_TOK_NUM) 3562 parse_number((char *)tokc.str.data); 3563 } else if (tok == TOK_PPSTR) { 3564 if (parse_flags & PARSE_FLAG_TOK_STR) 3565 parse_string((char *)tokc.str.data, tokc.str.size - 1); 3000 3566 } 3001 3567 } … … 3005 3571 ST_INLN void unget_tok(int last_tok) 3006 3572 { 3007 int i, n; 3008 int *q; 3009 if (unget_buffer_enabled) 3010 { 3011 /* assert(macro_ptr == unget_saved_buffer + 1); 3012 assert(*macro_ptr == 0); */ 3013 } 3014 else 3015 { 3016 unget_saved_macro_ptr = macro_ptr; 3017 unget_buffer_enabled = 1; 3018 } 3019 q = unget_saved_buffer; 3020 macro_ptr = q; 3021 *q++ = tok; 3022 n = tok_ext_size(tok) - 1; 3023 for(i=0;i<n;i++) 3024 *q++ = tokc.tab[i]; 3025 *q = 0; /* end of token string */ 3573 3574 TokenString *str = tok_str_alloc(); 3575 tok_str_add2(str, tok, &tokc); 3576 tok_str_add(str, 0); 3577 begin_macro(str, 1); 3026 3578 tok = last_tok; 3027 3579 } 3028 3580 3029 3030 /* better than nothing, but needs extension to handle '-E' option 3031 correctly too */3032 ST_FUNC void preprocess_init(TCCState *s1) 3033 { 3581 ST_FUNC void preprocess_start(TCCState *s1, int is_asm) 3582 { 3583 CString cstr; 3584 int i; 3585 3034 3586 s1->include_stack_ptr = s1->include_stack; 3035 /* XXX: move that before to avoid having to initialize3036 file->ifdef_stack_ptr ? */3037 3587 s1->ifdef_stack_ptr = s1->ifdef_stack; 3038 3588 file->ifdef_stack_ptr = s1->ifdef_stack_ptr; 3039 3040 vtop = vstack - 1; 3589 pp_expr = 0; 3590 pp_counter = 0; 3591 pp_debug_tok = pp_debug_symv = 0; 3592 pp_once++; 3593 pvtop = vtop = vstack - 1; 3041 3594 s1->pack_stack[0] = 0; 3042 3595 s1->pack_stack_ptr = s1->pack_stack; 3043 } 3044 3045 ST_FUNC void preprocess_new(void) 3596 3597 set_idnum('$', s1->dollars_in_identifiers ? IS_ID : 0); 3598 set_idnum('.', is_asm ? IS_ID : 0); 3599 3600 cstr_new(&cstr); 3601 cstr_cat(&cstr, "\"", -1); 3602 cstr_cat(&cstr, file->filename, -1); 3603 cstr_cat(&cstr, "\"", 0); 3604 tcc_define_symbol(s1, "__BASE_FILE__", cstr.data); 3605 3606 cstr_reset(&cstr); 3607 for (i = 0; i < s1->nb_cmd_include_files; i++) { 3608 cstr_cat(&cstr, "#include \"", -1); 3609 cstr_cat(&cstr, s1->cmd_include_files[i], -1); 3610 cstr_cat(&cstr, "\"\n", -1); 3611 } 3612 if (cstr.size) { 3613 *s1->include_stack_ptr++ = file; 3614 tcc_open_bf(s1, "<command line>", cstr.size); 3615 memcpy(file->buffer, cstr.data, cstr.size); 3616 } 3617 cstr_free(&cstr); 3618 3619 if (is_asm) 3620 tcc_define_symbol(s1, "__ASSEMBLER__", NULL); 3621 3622 parse_flags = is_asm ? PARSE_FLAG_ASM_FILE : 0; 3623 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; 3624 } 3625 3626 /* cleanup from error/setjmp */ 3627 ST_FUNC void preprocess_end(TCCState *s1) 3628 { 3629 while (macro_stack) 3630 end_macro(); 3631 macro_ptr = NULL; 3632 } 3633 3634 ST_FUNC void tccpp_new(TCCState *s) 3046 3635 { 3047 3636 int i, c; 3048 3637 const char *p, *r; 3049 3638 3639 /* might be used in error() before preprocess_start() */ 3640 s->include_stack_ptr = s->include_stack; 3641 s->ppfp = stdout; 3642 3050 3643 /* init isid table */ 3051 for(i=CH_EOF;i<256;i++) 3052 isidnum_table[i-CH_EOF] = isid(i) || isnum(i); 3053 3054 /* add all tokens */ 3055 table_ident = NULL; 3644 for(i = CH_EOF; i<128; i++) 3645 set_idnum(i, 3646 is_space(i) ? IS_SPC 3647 : isid(i) ? IS_ID 3648 : isnum(i) ? IS_NUM 3649 : 0); 3650 3651 for(i = 128; i<256; i++) 3652 set_idnum(i, IS_ID); 3653 3654 /* init allocators */ 3655 tal_new(&toksym_alloc, TOKSYM_TAL_LIMIT, TOKSYM_TAL_SIZE); 3656 tal_new(&tokstr_alloc, TOKSTR_TAL_LIMIT, TOKSTR_TAL_SIZE); 3657 tal_new(&cstr_alloc, CSTR_TAL_LIMIT, CSTR_TAL_SIZE); 3658 3056 3659 memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *)); 3660 cstr_new(&cstr_buf); 3661 cstr_realloc(&cstr_buf, STRING_MAX_SIZE); 3662 tok_str_new(&tokstr_buf); 3663 tok_str_realloc(&tokstr_buf, TOKSTR_MAX_SIZE); 3057 3664 3058 3665 tok_ident = TOK_IDENT; … … 3070 3677 } 3071 3678 3679 ST_FUNC void tccpp_delete(TCCState *s) 3680 { 3681 int i, n; 3682 3683 /* free -D and compiler defines */ 3684 free_defines(NULL); 3685 3686 /* free tokens */ 3687 n = tok_ident - TOK_IDENT; 3688 for(i = 0; i < n; i++) 3689 tal_free(toksym_alloc, table_ident[i]); 3690 tcc_free(table_ident); 3691 table_ident = NULL; 3692 3693 /* free static buffers */ 3694 cstr_free(&tokcstr); 3695 cstr_free(&cstr_buf); 3696 cstr_free(¯o_equal_buf); 3697 tok_str_free_str(tokstr_buf.str); 3698 3699 /* free allocators */ 3700 tal_delete(toksym_alloc); 3701 toksym_alloc = NULL; 3702 tal_delete(tokstr_alloc); 3703 tokstr_alloc = NULL; 3704 tal_delete(cstr_alloc); 3705 cstr_alloc = NULL; 3706 } 3707 3708 /* ------------------------------------------------------------------------- */ 3709 /* tcc -E [-P[1]] [-dD} support */ 3710 3711 static void tok_print(const char *msg, const int *str) 3712 { 3713 FILE *fp; 3714 int t, s = 0; 3715 CValue cval; 3716 3717 fp = tcc_state->ppfp; 3718 fprintf(fp, "%s", msg); 3719 while (str) { 3720 TOK_GET(&t, &str, &cval); 3721 if (!t) 3722 break; 3723 fprintf(fp, " %s" + s, get_tok_str(t, &cval)), s = 1; 3724 } 3725 fprintf(fp, "\n"); 3726 } 3727 3728 static void pp_line(TCCState *s1, BufferedFile *f, int level) 3729 { 3730 int d = f->line_num - f->line_ref; 3731 3732 if (s1->dflag & 4) 3733 return; 3734 3735 if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_NONE) { 3736 ; 3737 } else if (level == 0 && f->line_ref && d < 8) { 3738 while (d > 0) 3739 fputs("\n", s1->ppfp), --d; 3740 } else if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_STD) { 3741 fprintf(s1->ppfp, "#line %d \"%s\"\n", f->line_num, f->filename); 3742 } else { 3743 fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename, 3744 level > 0 ? " 1" : level < 0 ? " 2" : ""); 3745 } 3746 f->line_ref = f->line_num; 3747 } 3748 3749 static void define_print(TCCState *s1, int v) 3750 { 3751 FILE *fp; 3752 Sym *s; 3753 3754 s = define_find(v); 3755 if (NULL == s || NULL == s->d) 3756 return; 3757 3758 fp = s1->ppfp; 3759 fprintf(fp, "#define %s", get_tok_str(v, NULL)); 3760 if (s->type.t == MACRO_FUNC) { 3761 Sym *a = s->next; 3762 fprintf(fp,"("); 3763 if (a) 3764 for (;;) { 3765 fprintf(fp,"%s", get_tok_str(a->v & ~SYM_FIELD, NULL)); 3766 if (!(a = a->next)) 3767 break; 3768 fprintf(fp,","); 3769 } 3770 fprintf(fp,")"); 3771 } 3772 tok_print("", s->d); 3773 } 3774 3775 static void pp_debug_defines(TCCState *s1) 3776 { 3777 int v, t; 3778 const char *vs; 3779 FILE *fp; 3780 3781 t = pp_debug_tok; 3782 if (t == 0) 3783 return; 3784 3785 file->line_num--; 3786 pp_line(s1, file, 0); 3787 file->line_ref = ++file->line_num; 3788 3789 fp = s1->ppfp; 3790 v = pp_debug_symv; 3791 vs = get_tok_str(v, NULL); 3792 if (t == TOK_DEFINE) { 3793 define_print(s1, v); 3794 } else if (t == TOK_UNDEF) { 3795 fprintf(fp, "#undef %s\n", vs); 3796 } else if (t == TOK_push_macro) { 3797 fprintf(fp, "#pragma push_macro(\"%s\")\n", vs); 3798 } else if (t == TOK_pop_macro) { 3799 fprintf(fp, "#pragma pop_macro(\"%s\")\n", vs); 3800 } 3801 pp_debug_tok = 0; 3802 } 3803 3804 static void pp_debug_builtins(TCCState *s1) 3805 { 3806 int v; 3807 for (v = TOK_IDENT; v < tok_ident; ++v) 3808 define_print(s1, v); 3809 } 3810 3811 /* Add a space between tokens a and b to avoid unwanted textual pasting */ 3812 static int pp_need_space(int a, int b) 3813 { 3814 return 'E' == a ? '+' == b || '-' == b 3815 : '+' == a ? TOK_INC == b || '+' == b 3816 : '-' == a ? TOK_DEC == b || '-' == b 3817 : a >= TOK_IDENT ? b >= TOK_IDENT 3818 : a == TOK_PPNUM ? b >= TOK_IDENT 3819 : 0; 3820 } 3821 3822 /* maybe hex like 0x1e */ 3823 static int pp_check_he0xE(int t, const char *p) 3824 { 3825 if (t == TOK_PPNUM && toup(strchr(p, 0)[-1]) == 'E') 3826 return 'E'; 3827 return t; 3828 } 3829 3072 3830 /* Preprocess the current file */ 3073 3831 ST_FUNC int tcc_preprocess(TCCState *s1) 3074 3832 { 3075 Sym *define_start; 3076 3077 BufferedFile *file_ref, **iptr, **iptr_new; 3078 int token_seen, line_ref, d; 3079 const char *s; 3080 3081 preprocess_init(s1); 3082 define_start = define_stack; 3083 ch = file->buf_ptr[0]; 3084 tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; 3085 parse_flags = PARSE_FLAG_ASM_COMMENTS | PARSE_FLAG_PREPROCESS | 3086 PARSE_FLAG_LINEFEED | PARSE_FLAG_SPACES; 3087 token_seen = 0; 3088 line_ref = 0; 3089 file_ref = NULL; 3090 iptr = s1->include_stack_ptr; 3091 3833 BufferedFile **iptr; 3834 int token_seen, spcs, level; 3835 const char *p; 3836 char white[400]; 3837 3838 parse_flags = PARSE_FLAG_PREPROCESS 3839 | (parse_flags & PARSE_FLAG_ASM_FILE) 3840 | PARSE_FLAG_LINEFEED 3841 | PARSE_FLAG_SPACES 3842 | PARSE_FLAG_ACCEPT_STRAYS 3843 ; 3844 /* Credits to Fabrice Bellard's initial revision to demonstrate its 3845 capability to compile and run itself, provided all numbers are 3846 given as decimals. tcc -E -P10 will do. */ 3847 if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_P10) 3848 parse_flags |= PARSE_FLAG_TOK_NUM, s1->Pflag = 1; 3849 3850 #ifdef PP_BENCH 3851 /* for PP benchmarks */ 3852 do next(); while (tok != TOK_EOF); 3853 return 0; 3854 #endif 3855 3856 if (s1->dflag & 1) { 3857 pp_debug_builtins(s1); 3858 s1->dflag &= ~1; 3859 } 3860 3861 token_seen = TOK_LINEFEED, spcs = 0; 3862 pp_line(s1, file, 0); 3092 3863 for (;;) { 3864 iptr = s1->include_stack_ptr; 3093 3865 next(); 3094 if (tok == TOK_EOF) {3866 if (tok == TOK_EOF) 3095 3867 break; 3096 } else if (file != file_ref) { 3097 goto print_line; 3868 3869 level = s1->include_stack_ptr - iptr; 3870 if (level) { 3871 if (level > 0) 3872 pp_line(s1, *iptr, 0); 3873 pp_line(s1, file, level); 3874 } 3875 if (s1->dflag & 7) { 3876 pp_debug_defines(s1); 3877 if (s1->dflag & 4) 3878 continue; 3879 } 3880 3881 if (is_space(tok)) { 3882 if (spcs < sizeof white - 1) 3883 white[spcs++] = tok; 3884 continue; 3098 3885 } else if (tok == TOK_LINEFEED) { 3099 if (!token_seen) 3886 spcs = 0; 3887 if (token_seen == TOK_LINEFEED) 3100 3888 continue; 3101 ++line_ref; 3102 token_seen = 0; 3103 } else if (!token_seen) { 3104 d = file->line_num - line_ref; 3105 if (file != file_ref || d < 0 || d >= 8) { 3106 print_line: 3107 iptr_new = s1->include_stack_ptr; 3108 s = iptr_new > iptr ? " 1" 3109 : iptr_new < iptr ? " 2" 3110 : iptr_new > s1->include_stack ? " 3" 3111 : "" 3112 ; 3113 iptr = iptr_new; 3114 fprintf(s1->ppfp, "# %d \"%s\"%s\n", file->line_num, file->filename, s); 3115 } else { 3116 while (d) 3117 fputs("\n", s1->ppfp), --d; 3118 } 3119 line_ref = (file_ref = file)->line_num; 3120 token_seen = tok != TOK_LINEFEED; 3121 if (!token_seen) 3122 continue; 3123 } 3124 fputs(get_tok_str(tok, &tokc), s1->ppfp); 3125 } 3126 free_defines(define_start); 3889 ++file->line_ref; 3890 } else if (token_seen == TOK_LINEFEED) { 3891 pp_line(s1, file, 0); 3892 } else if (spcs == 0 && pp_need_space(token_seen, tok)) { 3893 white[spcs++] = ' '; 3894 } 3895 3896 white[spcs] = 0, fputs(white, s1->ppfp), spcs = 0; 3897 fputs(p = get_tok_str(tok, &tokc), s1->ppfp); 3898 token_seen = pp_check_he0xE(tok, p); 3899 } 3127 3900 return 0; 3128 3901 } 3902 3903 /* ------------------------------------------------------------------------- */
Note:
See TracChangeset
for help on using the changeset viewer.