1 /****************************************************************************** 2 * 3 * Module Name: aslsupport.l - Flex/lex scanner C support routines. 4 * NOTE: Included into aslcompile.l, not compiled by itself. 5 * 6 *****************************************************************************/ 7 8 /* 9 * Copyright (C) 2000 - 2015, Intel Corp. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions, and the following disclaimer, 17 * without modification. 18 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 19 * substantially similar to the "NO WARRANTY" disclaimer below 20 * ("Disclaimer") and any redistribution must be conditioned upon 21 * including a substantially similar Disclaimer requirement for further 22 * binary redistribution. 23 * 3. Neither the names of the above-listed copyright holders nor the names 24 * of any contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * Alternatively, this software may be distributed under the terms of the 28 * GNU General Public License ("GPL") version 2 as published by the Free 29 * Software Foundation. 30 * 31 * NO WARRANTY 32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 33 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 34 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 35 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 36 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 41 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 42 * POSSIBILITY OF SUCH DAMAGES. 43 */ 44 45 /* Configuration */ 46 47 #define ASL_SPACES_PER_TAB 4 48 49 #define ASL_NORMAL_CHAR 0 50 #define ASL_ESCAPE_SEQUENCE 1 51 #define ASL_OCTAL_CONSTANT 2 52 #define ASL_HEX_CONSTANT 3 53 54 55 /* File node - used for "Include" operator file stack */ 56 57 typedef struct asl_file_node 58 { 59 FILE *File; 60 UINT32 CurrentLineNumber; 61 YY_BUFFER_STATE State; 62 char *Filename; 63 struct asl_file_node *Next; 64 65 } ASL_FILE_NODE; 66 67 /* File stack for the "Include" operator (NOT #include operator) */ 68 69 ASL_FILE_NODE *Gbl_IncludeFileStack = NULL; 70 71 72 /******************************************************************************* 73 * 74 * FUNCTION: AslParserCleanup 75 * 76 * Used to delete the current buffer 77 * 78 ******************************************************************************/ 79 80 void 81 AslParserCleanup ( 82 void) 83 { 84 85 yy_delete_buffer (YY_CURRENT_BUFFER); 86 } 87 88 89 /******************************************************************************* 90 * 91 * FUNCTION: AslDoLineDirective 92 * 93 * PARAMETERS: None. Uses input() to access current source code line 94 * 95 * RETURN: Updates global line number and filename 96 * 97 * DESCRIPTION: Handle #line directives emitted by the preprocessor. 98 * 99 * The #line directive is emitted by the preprocesser, and is used to 100 * pass through line numbers from the original source code file to the 101 * preprocessor output file (.i). This allows any compiler-generated 102 * error messages to be displayed with the correct line number. 103 * 104 ******************************************************************************/ 105 106 static void 107 AslDoLineDirective ( 108 void) 109 { 110 int c; 111 char *Token; 112 UINT32 LineNumber; 113 char *Filename; 114 UINT32 i; 115 116 Gbl_HasIncludeFiles = TRUE; 117 118 /* Eat the entire line that contains the #line directive */ 119 120 Gbl_LineBufPtr = Gbl_CurrentLineBuffer; 121 122 while ((c = input()) != '\n' && c != EOF) 123 { 124 *Gbl_LineBufPtr = c; 125 Gbl_LineBufPtr++; 126 } 127 *Gbl_LineBufPtr = 0; 128 129 /* First argument is the actual line number */ 130 131 Token = strtok (Gbl_CurrentLineBuffer, " "); 132 if (!Token) 133 { 134 goto ResetAndExit; 135 } 136 137 /* First argument is the line number */ 138 139 LineNumber = (UINT32) UtDoConstant (Token); 140 141 /* Emit the appropriate number of newlines */ 142 143 Gbl_CurrentColumn = 0; 144 if (LineNumber > Gbl_CurrentLineNumber) 145 { 146 for (i = 0; i < (LineNumber - Gbl_CurrentLineNumber); i++) 147 { 148 FlWriteFile (ASL_FILE_SOURCE_OUTPUT, "\n", 1); 149 Gbl_CurrentColumn++; 150 } 151 } 152 153 FlSetLineNumber (LineNumber); 154 155 /* Second argument is the optional filename (in double quotes) */ 156 157 Token = strtok (NULL, " \""); 158 if (Token) 159 { 160 Filename = ACPI_ALLOCATE_ZEROED (strlen (Token) + 1); 161 strcpy (Filename, Token); 162 FlSetFilename (Filename); 163 } 164 165 /* Third argument is not supported at this time */ 166 167 ResetAndExit: 168 169 /* Reset globals for a new line */ 170 171 Gbl_CurrentLineOffset += Gbl_CurrentColumn; 172 Gbl_CurrentColumn = 0; 173 Gbl_LineBufPtr = Gbl_CurrentLineBuffer; 174 } 175 176 177 /******************************************************************************* 178 * 179 * FUNCTION: AslPopInputFileStack 180 * 181 * PARAMETERS: None 182 * 183 * RETURN: 0 if a node was popped, -1 otherwise 184 * 185 * DESCRIPTION: Pop the top of the input file stack and point the parser to 186 * the saved parse buffer contained in the fnode. Also, set the 187 * global line counters to the saved values. This function is 188 * called when an include file reaches EOF. 189 * 190 ******************************************************************************/ 191 192 int 193 AslPopInputFileStack ( 194 void) 195 { 196 ASL_FILE_NODE *Fnode; 197 198 199 Fnode = Gbl_IncludeFileStack; 200 DbgPrint (ASL_PARSE_OUTPUT, 201 "\nPop InputFile Stack, Fnode %p\n\n", Fnode); 202 203 if (!Fnode) 204 { 205 return (-1); 206 } 207 208 /* Close the current include file */ 209 210 fclose (yyin); 211 212 /* Update the top-of-stack */ 213 214 Gbl_IncludeFileStack = Fnode->Next; 215 216 /* Reset global line counter and filename */ 217 218 Gbl_Files[ASL_FILE_INPUT].Filename = Fnode->Filename; 219 Gbl_CurrentLineNumber = Fnode->CurrentLineNumber; 220 221 /* Point the parser to the popped file */ 222 223 yy_delete_buffer (YY_CURRENT_BUFFER); 224 yy_switch_to_buffer (Fnode->State); 225 226 /* All done with this node */ 227 228 ACPI_FREE (Fnode); 229 return (0); 230 } 231 232 233 /******************************************************************************* 234 * 235 * FUNCTION: AslPushInputFileStack 236 * 237 * PARAMETERS: InputFile - Open file pointer 238 * Filename - Name of the file 239 * 240 * RETURN: None 241 * 242 * DESCRIPTION: Push the InputFile onto the file stack, and point the parser 243 * to this file. Called when an include file is successfully 244 * opened. 245 * 246 ******************************************************************************/ 247 248 void 249 AslPushInputFileStack ( 250 FILE *InputFile, 251 char *Filename) 252 { 253 ASL_FILE_NODE *Fnode; 254 YY_BUFFER_STATE State; 255 256 257 /* Save the current state in an Fnode */ 258 259 Fnode = UtLocalCalloc (sizeof (ASL_FILE_NODE)); 260 261 Fnode->File = yyin; 262 Fnode->Next = Gbl_IncludeFileStack; 263 Fnode->State = YY_CURRENT_BUFFER; 264 Fnode->Filename = Gbl_Files[ASL_FILE_INPUT].Filename; 265 Fnode->CurrentLineNumber = Gbl_CurrentLineNumber; 266 267 /* Push it on the stack */ 268 269 Gbl_IncludeFileStack = Fnode; 270 271 /* Point the parser to this file */ 272 273 State = yy_create_buffer (InputFile, YY_BUF_SIZE); 274 yy_switch_to_buffer (State); 275 276 DbgPrint (ASL_PARSE_OUTPUT, 277 "\nPush InputFile Stack, returning %p\n\n", InputFile); 278 279 /* Reset the global line count and filename */ 280 281 Gbl_Files[ASL_FILE_INPUT].Filename = 282 UtStringCacheCalloc (strlen (Filename) + 1); 283 284 strcpy (Gbl_Files[ASL_FILE_INPUT].Filename, Filename); 285 286 Gbl_CurrentLineNumber = 1; 287 yyin = InputFile; 288 } 289 290 291 /******************************************************************************* 292 * 293 * FUNCTION: AslResetCurrentLineBuffer 294 * 295 * PARAMETERS: None 296 * 297 * RETURN: None 298 * 299 * DESCRIPTION: Reset the Line Buffer to zero, increment global line numbers. 300 * 301 ******************************************************************************/ 302 303 void 304 AslResetCurrentLineBuffer ( 305 void) 306 { 307 308 if (Gbl_Files[ASL_FILE_SOURCE_OUTPUT].Handle) 309 { 310 FlWriteFile (ASL_FILE_SOURCE_OUTPUT, Gbl_CurrentLineBuffer, 311 Gbl_LineBufPtr - Gbl_CurrentLineBuffer); 312 } 313 314 Gbl_CurrentLineOffset += Gbl_CurrentColumn; 315 Gbl_CurrentColumn = 0; 316 317 Gbl_CurrentLineNumber++; 318 Gbl_LogicalLineNumber++; 319 Gbl_LineBufPtr = Gbl_CurrentLineBuffer; 320 } 321 322 323 /******************************************************************************* 324 * 325 * FUNCTION: AslInsertLineBuffer 326 * 327 * PARAMETERS: SourceChar - One char from the input ASL source file 328 * 329 * RETURN: None 330 * 331 * DESCRIPTION: Put one character of the source file into the temp line buffer 332 * 333 ******************************************************************************/ 334 335 void 336 AslInsertLineBuffer ( 337 int SourceChar) 338 { 339 UINT32 i; 340 UINT32 Count = 1; 341 342 343 if (SourceChar == EOF) 344 { 345 return; 346 } 347 348 Gbl_InputByteCount++; 349 350 /* Handle tabs. Convert to spaces */ 351 352 if (SourceChar == '\t') 353 { 354 SourceChar = ' '; 355 Count = ASL_SPACES_PER_TAB - 356 (Gbl_CurrentColumn & (ASL_SPACES_PER_TAB-1)); 357 } 358 359 for (i = 0; i < Count; i++) 360 { 361 Gbl_CurrentColumn++; 362 363 /* Insert the character into the line buffer */ 364 365 *Gbl_LineBufPtr = (UINT8) SourceChar; 366 Gbl_LineBufPtr++; 367 368 if (Gbl_LineBufPtr > 369 (Gbl_CurrentLineBuffer + (Gbl_LineBufferSize - 1))) 370 { 371 #if 0 372 /* 373 * Warning if we have split a long source line. 374 * <Probably overkill> 375 */ 376 sprintf (MsgBuffer, "Max %u", Gbl_LineBufferSize); 377 AslCommonError (ASL_WARNING, ASL_MSG_LONG_LINE, 378 Gbl_CurrentLineNumber, Gbl_LogicalLineNumber, 379 Gbl_CurrentLineOffset, Gbl_CurrentColumn, 380 Gbl_Files[ASL_FILE_INPUT].Filename, MsgBuffer); 381 #endif 382 383 AslResetCurrentLineBuffer (); 384 } 385 else if (SourceChar == '\n') 386 { 387 /* End of line */ 388 389 AslResetCurrentLineBuffer (); 390 } 391 } 392 } 393 394 395 /******************************************************************************* 396 * 397 * FUNCTION: count 398 * 399 * PARAMETERS: yytext - Contains the matched keyword. 400 * Type - Keyword/Character type: 401 * 0 = anything except a keyword 402 * 1 = pseudo-keywords 403 * 2 = non-executable ASL keywords 404 * 3 = executable ASL keywords 405 * 406 * RETURN: None 407 * 408 * DESCRIPTION: Count keywords and put them into the line buffer 409 * 410 ******************************************************************************/ 411 412 static void 413 count ( 414 int Type) 415 { 416 int i; 417 418 419 switch (Type) 420 { 421 case 2: 422 423 TotalKeywords++; 424 TotalNamedObjects++; 425 break; 426 427 case 3: 428 429 TotalKeywords++; 430 TotalExecutableOpcodes++; 431 break; 432 433 default: 434 435 break; 436 } 437 438 for (i = 0; (yytext[i] != 0) && (yytext[i] != EOF); i++) 439 { 440 AslInsertLineBuffer (yytext[i]); 441 *Gbl_LineBufPtr = 0; 442 } 443 } 444 445 446 /******************************************************************************* 447 * 448 * FUNCTION: AslDoComment 449 * 450 * PARAMETERS: none 451 * 452 * RETURN: none 453 * 454 * DESCRIPTION: Process a standard comment. 455 * 456 ******************************************************************************/ 457 458 static char 459 AslDoComment ( 460 void) 461 { 462 int c; 463 int c1 = 0; 464 465 466 AslInsertLineBuffer ('/'); 467 AslInsertLineBuffer ('*'); 468 469 loop: 470 471 /* Eat chars until end-of-comment */ 472 473 while (((c = input ()) != '*') && (c != EOF)) 474 { 475 AslInsertLineBuffer (c); 476 c1 = c; 477 } 478 479 if (c == EOF) 480 { 481 goto EarlyEOF; 482 } 483 484 /* 485 * Check for nested comment -- can help catch cases where a previous 486 * comment was accidently left unterminated 487 */ 488 if ((c1 == '/') && (c == '*')) 489 { 490 AslCommonError (ASL_WARNING, ASL_MSG_NESTED_COMMENT, 491 Gbl_CurrentLineNumber, Gbl_LogicalLineNumber, 492 Gbl_InputByteCount, Gbl_CurrentColumn, 493 Gbl_Files[ASL_FILE_INPUT].Filename, NULL); 494 } 495 496 /* Comment is closed only if the NEXT character is a slash */ 497 498 AslInsertLineBuffer (c); 499 500 if (((c1 = input ()) != '/') && (c1 != EOF)) 501 { 502 unput(c1); 503 goto loop; 504 } 505 506 if (c1 == EOF) 507 { 508 goto EarlyEOF; 509 } 510 511 AslInsertLineBuffer (c1); 512 return (TRUE); 513 514 515 EarlyEOF: 516 /* 517 * Premature End-Of-File 518 */ 519 AslCommonError (ASL_ERROR, ASL_MSG_EARLY_EOF, 520 Gbl_CurrentLineNumber, Gbl_LogicalLineNumber, 521 Gbl_CurrentLineOffset, Gbl_CurrentColumn, 522 Gbl_Files[ASL_FILE_INPUT].Filename, NULL); 523 return (FALSE); 524 } 525 526 527 /******************************************************************************* 528 * 529 * FUNCTION: AslDoCommentType2 530 * 531 * PARAMETERS: none 532 * 533 * RETURN: none 534 * 535 * DESCRIPTION: Process a new "//" comment. 536 * 537 ******************************************************************************/ 538 539 static char 540 AslDoCommentType2 ( 541 void) 542 { 543 int c; 544 545 546 AslInsertLineBuffer ('/'); 547 AslInsertLineBuffer ('/'); 548 549 while (((c = input ()) != '\n') && (c != EOF)) 550 { 551 AslInsertLineBuffer (c); 552 } 553 554 if (c == EOF) 555 { 556 /* End of file is OK, change to newline. Let parser detect EOF later */ 557 558 c = '\n'; 559 } 560 561 AslInsertLineBuffer (c); 562 return (TRUE); 563 } 564 565 566 /******************************************************************************* 567 * 568 * FUNCTION: AslDoStringLiteral 569 * 570 * PARAMETERS: none 571 * 572 * RETURN: none 573 * 574 * DESCRIPTION: Process a string literal (surrounded by quotes) 575 * 576 ******************************************************************************/ 577 578 static char 579 AslDoStringLiteral ( 580 void) 581 { 582 char *StringBuffer = MsgBuffer; 583 char *EndBuffer = MsgBuffer + ASL_MSG_BUFFER_SIZE; 584 char *CleanString; 585 int StringChar; 586 UINT32 State = ASL_NORMAL_CHAR; 587 UINT32 i = 0; 588 UINT8 Digit; 589 char ConvertBuffer[4]; 590 591 592 /* 593 * Eat chars until end-of-literal. 594 * NOTE: Put back the original surrounding quotes into the 595 * source line buffer. 596 */ 597 AslInsertLineBuffer ('\"'); 598 while ((StringChar = input()) != EOF) 599 { 600 AslInsertLineBuffer (StringChar); 601 602 DoCharacter: 603 switch (State) 604 { 605 case ASL_NORMAL_CHAR: 606 607 switch (StringChar) 608 { 609 case '\\': 610 /* 611 * Special handling for backslash-escape sequence. We will 612 * toss the backslash and translate the escape char(s). 613 */ 614 State = ASL_ESCAPE_SEQUENCE; 615 continue; 616 617 case '\"': 618 619 /* String terminator */ 620 621 goto CompletedString; 622 623 default: 624 625 break; 626 } 627 break; 628 629 630 case ASL_ESCAPE_SEQUENCE: 631 632 State = ASL_NORMAL_CHAR; 633 switch (StringChar) 634 { 635 case 'a': 636 637 StringChar = 0x07; /* BELL */ 638 break; 639 640 case 'b': 641 642 StringChar = 0x08; /* BACKSPACE */ 643 break; 644 645 case 'f': 646 647 StringChar = 0x0C; /* FORMFEED */ 648 break; 649 650 case 'n': 651 652 StringChar = 0x0A; /* LINEFEED */ 653 break; 654 655 case 'r': 656 657 StringChar = 0x0D; /* CARRIAGE RETURN*/ 658 break; 659 660 case 't': 661 662 StringChar = 0x09; /* HORIZONTAL TAB */ 663 break; 664 665 case 'v': 666 667 StringChar = 0x0B; /* VERTICAL TAB */ 668 break; 669 670 case 'x': 671 672 State = ASL_HEX_CONSTANT; 673 i = 0; 674 continue; 675 676 case '\'': /* Single Quote */ 677 case '\"': /* Double Quote */ 678 case '\\': /* Backslash */ 679 680 break; 681 682 default: 683 684 /* Check for an octal digit (0-7) */ 685 686 if (ACPI_IS_OCTAL_DIGIT (StringChar)) 687 { 688 State = ASL_OCTAL_CONSTANT; 689 ConvertBuffer[0] = StringChar; 690 i = 1; 691 continue; 692 } 693 694 /* Unknown escape sequence issue warning, but use the character */ 695 696 AslCommonError (ASL_WARNING, ASL_MSG_INVALID_ESCAPE, 697 Gbl_CurrentLineNumber, Gbl_LogicalLineNumber, 698 Gbl_CurrentLineOffset, Gbl_CurrentColumn, 699 Gbl_Files[ASL_FILE_INPUT].Filename, NULL); 700 break; 701 } 702 break; 703 704 705 case ASL_OCTAL_CONSTANT: 706 707 /* Up to three octal digits allowed */ 708 709 if (!ACPI_IS_OCTAL_DIGIT (StringChar) || 710 (i > 2)) 711 { 712 /* 713 * Reached end of the constant. Convert the assembled ASCII 714 * string and resume processing of the next character 715 */ 716 ConvertBuffer[i] = 0; 717 Digit = (UINT8) strtoul (ConvertBuffer, NULL, 8); 718 719 /* Check for NULL or non-ascii character (ignore if so) */ 720 721 if ((Digit == 0) || (Digit > ACPI_ASCII_MAX)) 722 { 723 AslCommonError (ASL_WARNING, ASL_MSG_INVALID_STRING, 724 Gbl_CurrentLineNumber, Gbl_LogicalLineNumber, 725 Gbl_CurrentLineOffset, Gbl_CurrentColumn, 726 Gbl_Files[ASL_FILE_INPUT].Filename, NULL); 727 } 728 else 729 { 730 *StringBuffer = (char) Digit; 731 StringBuffer++; 732 if (StringBuffer >= EndBuffer) 733 { 734 goto BufferOverflow; 735 } 736 } 737 738 State = ASL_NORMAL_CHAR; 739 goto DoCharacter; 740 break; 741 } 742 743 /* Append another digit of the constant */ 744 745 ConvertBuffer[i] = StringChar; 746 i++; 747 continue; 748 749 case ASL_HEX_CONSTANT: 750 751 /* Up to two hex digits allowed */ 752 753 if (!isxdigit (StringChar) || 754 (i > 1)) 755 { 756 /* 757 * Reached end of the constant. Convert the assembled ASCII 758 * string and resume processing of the next character 759 */ 760 ConvertBuffer[i] = 0; 761 Digit = (UINT8) strtoul (ConvertBuffer, NULL, 16); 762 763 /* Check for NULL or non-ascii character (ignore if so) */ 764 765 if ((Digit == 0) || (Digit > ACPI_ASCII_MAX)) 766 { 767 AslCommonError (ASL_WARNING, ASL_MSG_INVALID_STRING, 768 Gbl_CurrentLineNumber, Gbl_LogicalLineNumber, 769 Gbl_CurrentLineOffset, Gbl_CurrentColumn, 770 Gbl_Files[ASL_FILE_INPUT].Filename, NULL); 771 } 772 else 773 { 774 *StringBuffer = (char) Digit; 775 StringBuffer++; 776 if (StringBuffer >= EndBuffer) 777 { 778 goto BufferOverflow; 779 } 780 } 781 782 State = ASL_NORMAL_CHAR; 783 goto DoCharacter; 784 break; 785 } 786 787 /* Append another digit of the constant */ 788 789 ConvertBuffer[i] = StringChar; 790 i++; 791 continue; 792 793 default: 794 795 break; 796 } 797 798 /* Save the finished character */ 799 800 *StringBuffer = StringChar; 801 StringBuffer++; 802 if (StringBuffer >= EndBuffer) 803 { 804 goto BufferOverflow; 805 } 806 } 807 808 /* 809 * Premature End-Of-File 810 */ 811 AslCommonError (ASL_ERROR, ASL_MSG_EARLY_EOF, 812 Gbl_CurrentLineNumber, Gbl_LogicalLineNumber, 813 Gbl_CurrentLineOffset, Gbl_CurrentColumn, 814 Gbl_Files[ASL_FILE_INPUT].Filename, NULL); 815 return (FALSE); 816 817 818 CompletedString: 819 /* 820 * Null terminate the input string and copy string to a new buffer 821 */ 822 *StringBuffer = 0; 823 824 CleanString = UtStringCacheCalloc (strlen (MsgBuffer) + 1); 825 if (!CleanString) 826 { 827 AslCommonError (ASL_ERROR, ASL_MSG_MEMORY_ALLOCATION, 828 Gbl_CurrentLineNumber, Gbl_LogicalLineNumber, 829 Gbl_CurrentLineOffset, Gbl_CurrentColumn, 830 Gbl_Files[ASL_FILE_INPUT].Filename, NULL); 831 return (FALSE); 832 } 833 834 strcpy (CleanString, MsgBuffer); 835 AslCompilerlval.s = CleanString; 836 return (TRUE); 837 838 839 BufferOverflow: 840 841 /* Literal was too long */ 842 843 AslCommonError (ASL_ERROR, ASL_MSG_STRING_LENGTH, 844 Gbl_CurrentLineNumber, Gbl_LogicalLineNumber, 845 Gbl_CurrentLineOffset, Gbl_CurrentColumn, 846 Gbl_Files[ASL_FILE_INPUT].Filename, "Max length 4096"); 847 return (FALSE); 848 } 849