1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 1985 Sun Microsystems, Inc. 5 * Copyright (c) 1980, 1993 6 * The Regents of the University of California. All rights reserved. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #if 0 39 #ifndef lint 40 static char sccsid[] = "@(#)lexi.c 8.1 (Berkeley) 6/6/93"; 41 #endif /* not lint */ 42 #endif 43 #include <sys/cdefs.h> 44 __FBSDID("$FreeBSD$"); 45 46 /* 47 * Here we have the token scanner for indent. It scans off one token and puts 48 * it in the global variable "token". It returns a code, indicating the type 49 * of token scanned. 50 */ 51 52 #include <err.h> 53 #include <stdio.h> 54 #include <ctype.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <sys/param.h> 58 59 #include "indent_globs.h" 60 #include "indent_codes.h" 61 #include "indent.h" 62 63 struct templ { 64 const char *rwd; 65 int rwcode; 66 }; 67 68 /* 69 * This table has to be sorted alphabetically, because it'll be used in binary 70 * search. For the same reason, string must be the first thing in struct templ. 71 */ 72 struct templ specials[] = 73 { 74 {"_Bool", 4}, 75 {"_Complex", 4}, 76 {"_Imaginary", 4}, 77 {"auto", 10}, 78 {"bool", 4}, 79 {"break", 9}, 80 {"case", 8}, 81 {"char", 4}, 82 {"complex", 4}, 83 {"const", 4}, 84 {"continue", 12}, 85 {"default", 8}, 86 {"do", 6}, 87 {"double", 4}, 88 {"else", 6}, 89 {"enum", 3}, 90 {"extern", 10}, 91 {"float", 4}, 92 {"for", 5}, 93 {"global", 4}, 94 {"goto", 9}, 95 {"if", 5}, 96 {"imaginary", 4}, 97 {"inline", 12}, 98 {"int", 4}, 99 {"long", 4}, 100 {"offsetof", 1}, 101 {"register", 10}, 102 {"restrict", 12}, 103 {"return", 9}, 104 {"short", 4}, 105 {"signed", 4}, 106 {"sizeof", 2}, 107 {"static", 10}, 108 {"struct", 3}, 109 {"switch", 7}, 110 {"typedef", 11}, 111 {"union", 3}, 112 {"unsigned", 4}, 113 {"void", 4}, 114 {"volatile", 4}, 115 {"while", 5} 116 }; 117 118 const char **typenames; 119 int typename_count; 120 int typename_top = -1; 121 122 /* 123 * The transition table below was rewritten by hand from lx's output, given 124 * the following definitions. lx is Katherine Flavel's lexer generator. 125 * 126 * O = /[0-7]/; D = /[0-9]/; NZ = /[1-9]/; 127 * H = /[a-f0-9]/i; B = /[0-1]/; HP = /0x/i; 128 * BP = /0b/i; E = /e[+\-]?/i D+; P = /p[+\-]?/i D+; 129 * FS = /[fl]/i; IS = /u/i /(l|L|ll|LL)/? | /(l|L|ll|LL)/ /u/i?; 130 * 131 * D+ E FS? -> $float; 132 * D* "." D+ E? FS? -> $float; 133 * D+ "." E? FS? -> $float; HP H+ IS? -> $int; 134 * HP H+ P FS? -> $float; NZ D* IS? -> $int; 135 * HP H* "." H+ P FS? -> $float; "0" O* IS? -> $int; 136 * HP H+ "." P FS -> $float; BP B+ IS? -> $int; 137 */ 138 static char const *table[] = { 139 /* examples: 140 00 141 s 0xx 142 t 00xaa 143 a 11 101100xxa.. 144 r 11ee0001101lbuuxx.a.pp 145 t.01.e+008bLuxll0Ll.aa.p+0 146 states: ABCDEFGHIJKLMNOPQRSTUVWXYZ */ 147 ['0'] = "CEIDEHHHIJQ U Q VUVVZZZ", 148 ['1'] = "DEIDEHHHIJQ U Q VUVVZZZ", 149 ['7'] = "DEIDEHHHIJ U VUVVZZZ", 150 ['9'] = "DEJDEHHHJJ U VUVVZZZ", 151 ['a'] = " U VUVV ", 152 ['b'] = " K U VUVV ", 153 ['e'] = " FFF FF U VUVV ", 154 ['f'] = " f f U VUVV f", 155 ['u'] = " MM M i iiM M ", 156 ['x'] = " N ", 157 ['p'] = " FFX ", 158 ['L'] = " LLf fL PR Li L f", 159 ['l'] = " OOf fO S P O i O f", 160 ['+'] = " G Y ", 161 ['.'] = "B EE EE T W ", 162 /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */ 163 [0] = "uuiifuufiuuiiuiiiiiuiuuuuu", 164 }; 165 166 static int 167 strcmp_type(const void *e1, const void *e2) 168 { 169 return (strcmp(e1, *(const char * const *)e2)); 170 } 171 172 int 173 lexi(struct parser_state *state) 174 { 175 int unary_delim; /* this is set to 1 if the current token 176 * forces a following operator to be unary */ 177 int code; /* internal code to be returned */ 178 char qchar; /* the delimiter character for a string */ 179 180 e_token = s_token; /* point to start of place to save token */ 181 unary_delim = false; 182 state->col_1 = state->last_nl; /* tell world that this token started 183 * in column 1 iff the last thing 184 * scanned was a newline */ 185 state->last_nl = false; 186 187 while (*buf_ptr == ' ' || *buf_ptr == '\t') { /* get rid of blanks */ 188 state->col_1 = false; /* leading blanks imply token is not in column 189 * 1 */ 190 if (++buf_ptr >= buf_end) 191 fill_buffer(); 192 } 193 194 /* Scan an alphanumeric token */ 195 if (isalnum((unsigned char)*buf_ptr) || 196 (buf_ptr[0] == '.' && isdigit((unsigned char)buf_ptr[1]))) { 197 /* 198 * we have a character or number 199 */ 200 struct templ *p; 201 202 if (isdigit((unsigned char)*buf_ptr) || 203 (buf_ptr[0] == '.' && isdigit((unsigned char)buf_ptr[1]))) { 204 char s; 205 unsigned char i; 206 207 for (s = 'A'; s != 'f' && s != 'i' && s != 'u'; ) { 208 i = (unsigned char)*buf_ptr; 209 if (i >= nitems(table) || table[i] == NULL || 210 table[i][s - 'A'] == ' ') { 211 s = table[0][s - 'A']; 212 break; 213 } 214 s = table[i][s - 'A']; 215 CHECK_SIZE_TOKEN(1); 216 *e_token++ = *buf_ptr++; 217 if (buf_ptr >= buf_end) 218 fill_buffer(); 219 } 220 /* s now indicates the type: f(loating), i(integer), u(nknown) */ 221 } 222 else 223 while (isalnum((unsigned char)*buf_ptr) || 224 *buf_ptr == BACKSLASH || 225 *buf_ptr == '_') { 226 /* fill_buffer() terminates buffer with newline */ 227 if (*buf_ptr == BACKSLASH) { 228 if (*(buf_ptr + 1) == '\n') { 229 buf_ptr += 2; 230 if (buf_ptr >= buf_end) 231 fill_buffer(); 232 } else 233 break; 234 } 235 CHECK_SIZE_TOKEN(1); 236 /* copy it over */ 237 *e_token++ = *buf_ptr++; 238 if (buf_ptr >= buf_end) 239 fill_buffer(); 240 } 241 *e_token = '\0'; 242 243 if (s_token[0] == 'L' && s_token[1] == '\0' && 244 (*buf_ptr == '"' || *buf_ptr == '\'')) 245 return (strpfx); 246 247 while (*buf_ptr == ' ' || *buf_ptr == '\t') { /* get rid of blanks */ 248 if (++buf_ptr >= buf_end) 249 fill_buffer(); 250 } 251 state->keyword = 0; 252 if (state->last_token == structure && !state->p_l_follow) { 253 /* if last token was 'struct' and we're not 254 * in parentheses, then this token 255 * should be treated as a declaration */ 256 state->last_u_d = true; 257 return (decl); 258 } 259 /* 260 * Operator after identifier is binary unless last token was 'struct' 261 */ 262 state->last_u_d = (state->last_token == structure); 263 264 p = bsearch(s_token, 265 specials, 266 sizeof(specials) / sizeof(specials[0]), 267 sizeof(specials[0]), 268 strcmp_type); 269 if (p == NULL) { /* not a special keyword... */ 270 char *u; 271 272 /* ... so maybe a type_t or a typedef */ 273 if ((opt.auto_typedefs && ((u = strrchr(s_token, '_')) != NULL) && 274 strcmp(u, "_t") == 0) || (typename_top >= 0 && 275 bsearch(s_token, typenames, typename_top + 1, 276 sizeof(typenames[0]), strcmp_type))) { 277 state->keyword = 4; /* a type name */ 278 state->last_u_d = true; 279 goto found_typename; 280 } 281 } else { /* we have a keyword */ 282 state->keyword = p->rwcode; 283 state->last_u_d = true; 284 switch (p->rwcode) { 285 case 7: /* it is a switch */ 286 return (swstmt); 287 case 8: /* a case or default */ 288 return (casestmt); 289 290 case 3: /* a "struct" */ 291 /* FALLTHROUGH */ 292 case 4: /* one of the declaration keywords */ 293 found_typename: 294 if (state->p_l_follow) { 295 /* inside parens: cast, param list, offsetof or sizeof */ 296 state->cast_mask |= (1 << state->p_l_follow) & ~state->not_cast_mask; 297 } 298 if (state->last_token == period || state->last_token == unary_op) { 299 state->keyword = 0; 300 break; 301 } 302 if (p != NULL && p->rwcode == 3) 303 return (structure); 304 if (state->p_l_follow) 305 break; 306 return (decl); 307 308 case 5: /* if, while, for */ 309 return (sp_paren); 310 311 case 6: /* do, else */ 312 return (sp_nparen); 313 314 case 10: /* storage class specifier */ 315 return (storage); 316 317 case 11: /* typedef */ 318 return (type_def); 319 320 default: /* all others are treated like any other 321 * identifier */ 322 return (ident); 323 } /* end of switch */ 324 } /* end of if (found_it) */ 325 if (*buf_ptr == '(' && state->tos <= 1 && state->ind_level == 0 && 326 state->in_parameter_declaration == 0 && state->block_init == 0) { 327 char *tp = buf_ptr; 328 while (tp < buf_end) 329 if (*tp++ == ')' && (*tp == ';' || *tp == ',')) 330 goto not_proc; 331 strncpy(state->procname, token, sizeof state->procname - 1); 332 if (state->in_decl) 333 state->in_parameter_declaration = 1; 334 return (funcname); 335 not_proc:; 336 } 337 /* 338 * The following hack attempts to guess whether or not the current 339 * token is in fact a declaration keyword -- one that has been 340 * typedefd 341 */ 342 else if (!state->p_l_follow && !state->block_init && 343 !state->in_stmt && 344 ((*buf_ptr == '*' && buf_ptr[1] != '=') || 345 isalpha((unsigned char)*buf_ptr)) && 346 (state->last_token == semicolon || state->last_token == lbrace || 347 state->last_token == rbrace)) { 348 state->keyword = 4; /* a type name */ 349 state->last_u_d = true; 350 return decl; 351 } 352 if (state->last_token == decl) /* if this is a declared variable, 353 * then following sign is unary */ 354 state->last_u_d = true; /* will make "int a -1" work */ 355 return (ident); /* the ident is not in the list */ 356 } /* end of procesing for alpanum character */ 357 358 /* Scan a non-alphanumeric token */ 359 360 CHECK_SIZE_TOKEN(3); /* things like "<<=" */ 361 *e_token++ = *buf_ptr; /* if it is only a one-character token, it is 362 * moved here */ 363 *e_token = '\0'; 364 if (++buf_ptr >= buf_end) 365 fill_buffer(); 366 367 switch (*token) { 368 case '\n': 369 unary_delim = state->last_u_d; 370 state->last_nl = true; /* remember that we just had a newline */ 371 code = (had_eof ? 0 : newline); 372 373 /* 374 * if data has been exhausted, the newline is a dummy, and we should 375 * return code to stop 376 */ 377 break; 378 379 case '\'': /* start of quoted character */ 380 case '"': /* start of string */ 381 qchar = *token; 382 do { /* copy the string */ 383 while (1) { /* move one character or [/<char>]<char> */ 384 if (*buf_ptr == '\n') { 385 diag2(1, "Unterminated literal"); 386 goto stop_lit; 387 } 388 CHECK_SIZE_TOKEN(2); 389 *e_token = *buf_ptr++; 390 if (buf_ptr >= buf_end) 391 fill_buffer(); 392 if (*e_token == BACKSLASH) { /* if escape, copy extra char */ 393 if (*buf_ptr == '\n') /* check for escaped newline */ 394 ++line_no; 395 *++e_token = *buf_ptr++; 396 ++e_token; /* we must increment this again because we 397 * copied two chars */ 398 if (buf_ptr >= buf_end) 399 fill_buffer(); 400 } 401 else 402 break; /* we copied one character */ 403 } /* end of while (1) */ 404 } while (*e_token++ != qchar); 405 stop_lit: 406 code = ident; 407 break; 408 409 case ('('): 410 case ('['): 411 unary_delim = true; 412 code = lparen; 413 break; 414 415 case (')'): 416 case (']'): 417 code = rparen; 418 break; 419 420 case '#': 421 unary_delim = state->last_u_d; 422 code = preesc; 423 break; 424 425 case '?': 426 unary_delim = true; 427 code = question; 428 break; 429 430 case (':'): 431 code = colon; 432 unary_delim = true; 433 break; 434 435 case (';'): 436 unary_delim = true; 437 code = semicolon; 438 break; 439 440 case ('{'): 441 unary_delim = true; 442 443 /* 444 * if (state->in_or_st) state->block_init = 1; 445 */ 446 /* ? code = state->block_init ? lparen : lbrace; */ 447 code = lbrace; 448 break; 449 450 case ('}'): 451 unary_delim = true; 452 /* ? code = state->block_init ? rparen : rbrace; */ 453 code = rbrace; 454 break; 455 456 case 014: /* a form feed */ 457 unary_delim = state->last_u_d; 458 state->last_nl = true; /* remember this so we can set 'state->col_1' 459 * right */ 460 code = form_feed; 461 break; 462 463 case (','): 464 unary_delim = true; 465 code = comma; 466 break; 467 468 case '.': 469 unary_delim = false; 470 code = period; 471 break; 472 473 case '-': 474 case '+': /* check for -, +, --, ++ */ 475 code = (state->last_u_d ? unary_op : binary_op); 476 unary_delim = true; 477 478 if (*buf_ptr == token[0]) { 479 /* check for doubled character */ 480 *e_token++ = *buf_ptr++; 481 /* buffer overflow will be checked at end of loop */ 482 if (state->last_token == ident || state->last_token == rparen) { 483 code = (state->last_u_d ? unary_op : postop); 484 /* check for following ++ or -- */ 485 unary_delim = false; 486 } 487 } 488 else if (*buf_ptr == '=') 489 /* check for operator += */ 490 *e_token++ = *buf_ptr++; 491 else if (*buf_ptr == '>') { 492 /* check for operator -> */ 493 *e_token++ = *buf_ptr++; 494 unary_delim = false; 495 code = unary_op; 496 state->want_blank = false; 497 } 498 break; /* buffer overflow will be checked at end of 499 * switch */ 500 501 case '=': 502 if (state->in_or_st) 503 state->block_init = 1; 504 if (*buf_ptr == '=') {/* == */ 505 *e_token++ = '='; /* Flip =+ to += */ 506 buf_ptr++; 507 *e_token = 0; 508 } 509 code = binary_op; 510 unary_delim = true; 511 break; 512 /* can drop thru!!! */ 513 514 case '>': 515 case '<': 516 case '!': /* ops like <, <<, <=, !=, etc */ 517 if (*buf_ptr == '>' || *buf_ptr == '<' || *buf_ptr == '=') { 518 *e_token++ = *buf_ptr; 519 if (++buf_ptr >= buf_end) 520 fill_buffer(); 521 } 522 if (*buf_ptr == '=') 523 *e_token++ = *buf_ptr++; 524 code = (state->last_u_d ? unary_op : binary_op); 525 unary_delim = true; 526 break; 527 528 case '*': 529 unary_delim = true; 530 if (!state->last_u_d) { 531 if (*buf_ptr == '=') 532 *e_token++ = *buf_ptr++; 533 code = binary_op; 534 break; 535 } 536 while (*buf_ptr == '*' || isspace((unsigned char)*buf_ptr)) { 537 if (*buf_ptr == '*') { 538 CHECK_SIZE_TOKEN(1); 539 *e_token++ = *buf_ptr; 540 } 541 if (++buf_ptr >= buf_end) 542 fill_buffer(); 543 } 544 if (ps.in_decl) { 545 char *tp = buf_ptr; 546 547 while (isalpha((unsigned char)*tp) || 548 isspace((unsigned char)*tp)) { 549 if (++tp >= buf_end) 550 fill_buffer(); 551 } 552 if (*tp == '(') 553 ps.procname[0] = ' '; 554 } 555 code = unary_op; 556 break; 557 558 default: 559 if (token[0] == '/' && *buf_ptr == '*') { 560 /* it is start of comment */ 561 *e_token++ = '*'; 562 563 if (++buf_ptr >= buf_end) 564 fill_buffer(); 565 566 code = comment; 567 unary_delim = state->last_u_d; 568 break; 569 } 570 while (*(e_token - 1) == *buf_ptr || *buf_ptr == '=') { 571 /* 572 * handle ||, &&, etc, and also things as in int *****i 573 */ 574 CHECK_SIZE_TOKEN(1); 575 *e_token++ = *buf_ptr; 576 if (++buf_ptr >= buf_end) 577 fill_buffer(); 578 } 579 code = (state->last_u_d ? unary_op : binary_op); 580 unary_delim = true; 581 582 583 } /* end of switch */ 584 if (buf_ptr >= buf_end) /* check for input buffer empty */ 585 fill_buffer(); 586 state->last_u_d = unary_delim; 587 CHECK_SIZE_TOKEN(1); 588 *e_token = '\0'; /* null terminate the token */ 589 return (code); 590 } 591 592 /* Initialize constant transition table */ 593 void 594 init_constant_tt(void) 595 { 596 table['-'] = table['+']; 597 table['8'] = table['9']; 598 table['2'] = table['3'] = table['4'] = table['5'] = table['6'] = table['7']; 599 table['A'] = table['C'] = table['D'] = table['c'] = table['d'] = table['a']; 600 table['B'] = table['b']; 601 table['E'] = table['e']; 602 table['U'] = table['u']; 603 table['X'] = table['x']; 604 table['P'] = table['p']; 605 table['F'] = table['f']; 606 } 607 608 void 609 alloc_typenames(void) 610 { 611 612 typenames = (const char **)malloc(sizeof(typenames[0]) * 613 (typename_count = 16)); 614 if (typenames == NULL) 615 err(1, NULL); 616 } 617 618 void 619 add_typename(const char *key) 620 { 621 int comparison; 622 const char *copy; 623 624 if (typename_top + 1 >= typename_count) { 625 typenames = realloc((void *)typenames, 626 sizeof(typenames[0]) * (typename_count *= 2)); 627 if (typenames == NULL) 628 err(1, NULL); 629 } 630 if (typename_top == -1) 631 typenames[++typename_top] = copy = strdup(key); 632 else if ((comparison = strcmp(key, typenames[typename_top])) >= 0) { 633 /* take advantage of sorted input */ 634 if (comparison == 0) /* remove duplicates */ 635 return; 636 typenames[++typename_top] = copy = strdup(key); 637 } 638 else { 639 int p; 640 641 for (p = 0; (comparison = strcmp(key, typenames[p])) > 0; p++) 642 /* find place for the new key */; 643 if (comparison == 0) /* remove duplicates */ 644 return; 645 memmove(&typenames[p + 1], &typenames[p], 646 sizeof(typenames[0]) * (++typename_top - p)); 647 typenames[p] = copy = strdup(key); 648 } 649 650 if (copy == NULL) 651 err(1, NULL); 652 } 653