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