expr.c revision 95060
195060Sjmallett/* $OpenBSD: expr.c,v 1.12 2002/02/16 21:27:48 millert Exp $ */ 295060Sjmallett/* $NetBSD: expr.c,v 1.7 1995/09/28 05:37:31 tls Exp $ */ 395060Sjmallett 41590Srgrimes/* 51590Srgrimes * Copyright (c) 1989, 1993 61590Srgrimes * The Regents of the University of California. All rights reserved. 71590Srgrimes * 81590Srgrimes * This code is derived from software contributed to Berkeley by 91590Srgrimes * Ozan Yigit at York University. 101590Srgrimes * 111590Srgrimes * Redistribution and use in source and binary forms, with or without 121590Srgrimes * modification, are permitted provided that the following conditions 131590Srgrimes * are met: 141590Srgrimes * 1. Redistributions of source code must retain the above copyright 151590Srgrimes * notice, this list of conditions and the following disclaimer. 161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 171590Srgrimes * notice, this list of conditions and the following disclaimer in the 181590Srgrimes * documentation and/or other materials provided with the distribution. 191590Srgrimes * 3. All advertising materials mentioning features or use of this software 201590Srgrimes * must display the following acknowledgement: 211590Srgrimes * This product includes software developed by the University of 221590Srgrimes * California, Berkeley and its contributors. 231590Srgrimes * 4. Neither the name of the University nor the names of its contributors 241590Srgrimes * may be used to endorse or promote products derived from this software 251590Srgrimes * without specific prior written permission. 261590Srgrimes * 271590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 281590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 291590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 301590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 311590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 321590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 331590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 341590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 351590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 361590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 371590Srgrimes * SUCH DAMAGE. 381590Srgrimes */ 391590Srgrimes 4095060Sjmallett#include <sys/cdefs.h> 4195060Sjmallett__SCCSID("@(#)expr.c 8.2 (Berkeley) 4/29/95"); 4295060Sjmallett__RCSID_SOURCE("$OpenBSD: expr.c,v 1.12 2002/02/16 21:27:48 millert Exp $"); 4395060Sjmallett__FBSDID("$FreeBSD: head/usr.bin/m4/expr.c 95060 2002-04-19 17:26:21Z jmallett $"); 441590Srgrimes 4595060Sjmallett#include <sys/types.h> 4695060Sjmallett#include <ctype.h> 4795060Sjmallett#include <err.h> 4895060Sjmallett#include <stddef.h> 491590Srgrimes#include <stdio.h> 5095060Sjmallett#include "mdef.h" 5195060Sjmallett#include "extern.h" 521590Srgrimes 531590Srgrimes/* 541590Srgrimes * expression evaluator: performs a standard recursive 551590Srgrimes * descent parse to evaluate any expression permissible 561590Srgrimes * within the following grammar: 571590Srgrimes * 581590Srgrimes * expr : query EOS 591590Srgrimes * query : lor 601590Srgrimes * | lor "?" query ":" query 611590Srgrimes * lor : land { "||" land } 627896Sache * land : not { "&&" not } 637896Sache * not : eqrel 647896Sache * | '!' not 657896Sache * eqrel : shift { eqrelop shift } 661590Srgrimes * shift : primary { shop primary } 671590Srgrimes * primary : term { addop term } 687896Sache * term : exp { mulop exp } 697896Sache * exp : unary { expop unary } 701590Srgrimes * unary : factor 711590Srgrimes * | unop unary 721590Srgrimes * factor : constant 731590Srgrimes * | "(" query ")" 741590Srgrimes * constant: num 751590Srgrimes * | "'" CHAR "'" 761590Srgrimes * num : DIGIT 771590Srgrimes * | DIGIT num 781590Srgrimes * shop : "<<" 791590Srgrimes * | ">>" 807896Sache * eqrel : "=" 811590Srgrimes * | "==" 821590Srgrimes * | "!=" 837896Sache * | "<" 841590Srgrimes * | ">" 851590Srgrimes * | "<=" 861590Srgrimes * | ">=" 871590Srgrimes * 881590Srgrimes * 891590Srgrimes * This expression evaluator is lifted from a public-domain 901590Srgrimes * C Pre-Processor included with the DECUS C Compiler distribution. 911590Srgrimes * It is hacked somewhat to be suitable for m4. 921590Srgrimes * 931590Srgrimes * Originally by: Mike Lutz 941590Srgrimes * Bob Harper 951590Srgrimes */ 961590Srgrimes 971590Srgrimes#define EQL 0 981590Srgrimes#define NEQ 1 991590Srgrimes#define LSS 2 1001590Srgrimes#define LEQ 3 1011590Srgrimes#define GTR 4 1021590Srgrimes#define GEQ 5 1031590Srgrimes#define OCTAL 8 1041590Srgrimes#define DECIMAL 10 10595060Sjmallett#define HEX 16 1061590Srgrimes 10795060Sjmallettstatic const char *nxtch; /* Parser scan pointer */ 10895060Sjmallettstatic const char *where; 1091590Srgrimes 11092921Simpstatic int query(void); 11192921Simpstatic int lor(void); 11292921Simpstatic int land(void); 11392921Simpstatic int not(void); 11492921Simpstatic int eqrel(void); 11592921Simpstatic int shift(void); 11692921Simpstatic int primary(void); 11792921Simpstatic int term(void); 11892921Simpstatic int exp(void); 11992921Simpstatic int unary(void); 12092921Simpstatic int factor(void); 12192921Simpstatic int constant(void); 12292921Simpstatic int num(void); 12392921Simpstatic int geteqrel(void); 12492921Simpstatic int skipws(void); 12595060Sjmallettstatic void experr(const char *); 1261590Srgrimes 1271590Srgrimes/* 1281590Srgrimes * For longjmp 1291590Srgrimes */ 1301590Srgrimes#include <setjmp.h> 1311590Srgrimesstatic jmp_buf expjump; 1321590Srgrimes 1331590Srgrimes/* 1341590Srgrimes * macros: 1351590Srgrimes * ungetch - Put back the last character examined. 1361590Srgrimes * getch - return the next character from expr string. 1371590Srgrimes */ 1381590Srgrimes#define ungetch() nxtch-- 1391590Srgrimes#define getch() *nxtch++ 1401590Srgrimes 1411590Srgrimesint 1421590Srgrimesexpr(expbuf) 14395060Sjmallett const char *expbuf; 1441590Srgrimes{ 14595060Sjmallett int rval; 1461590Srgrimes 1471590Srgrimes nxtch = expbuf; 14895060Sjmallett where = expbuf; 1491590Srgrimes if (setjmp(expjump) != 0) 1501590Srgrimes return FALSE; 1511590Srgrimes 1521590Srgrimes rval = query(); 1531590Srgrimes if (skipws() == EOS) 1541590Srgrimes return rval; 1551590Srgrimes 1561590Srgrimes printf("m4: ill-formed expression.\n"); 1571590Srgrimes return FALSE; 1581590Srgrimes} 1591590Srgrimes 1601590Srgrimes/* 1611590Srgrimes * query : lor | lor '?' query ':' query 1621590Srgrimes */ 1631590Srgrimesstatic int 1641590Srgrimesquery() 1651590Srgrimes{ 16695060Sjmallett int bool, true_val, false_val; 1671590Srgrimes 1681590Srgrimes bool = lor(); 1691590Srgrimes if (skipws() != '?') { 1701590Srgrimes ungetch(); 1711590Srgrimes return bool; 1721590Srgrimes } 1731590Srgrimes 1741590Srgrimes true_val = query(); 1751590Srgrimes if (skipws() != ':') 1761590Srgrimes experr("bad query"); 1771590Srgrimes 1781590Srgrimes false_val = query(); 1791590Srgrimes return bool ? true_val : false_val; 1801590Srgrimes} 1811590Srgrimes 1821590Srgrimes/* 1831590Srgrimes * lor : land { '||' land } 1841590Srgrimes */ 1851590Srgrimesstatic int 1861590Srgrimeslor() 1871590Srgrimes{ 18895060Sjmallett int c, vl, vr; 1891590Srgrimes 1901590Srgrimes vl = land(); 1917896Sache while ((c = skipws()) == '|') { 1927896Sache if (getch() != '|') 1937896Sache ungetch(); 1941590Srgrimes vr = land(); 1951590Srgrimes vl = vl || vr; 1961590Srgrimes } 1971590Srgrimes 1981590Srgrimes ungetch(); 1991590Srgrimes return vl; 2001590Srgrimes} 2011590Srgrimes 2021590Srgrimes/* 2037896Sache * land : not { '&&' not } 2041590Srgrimes */ 2051590Srgrimesstatic int 2061590Srgrimesland() 2071590Srgrimes{ 20895060Sjmallett int c, vl, vr; 2091590Srgrimes 2107896Sache vl = not(); 2117896Sache while ((c = skipws()) == '&') { 2127896Sache if (getch() != '&') 2137896Sache ungetch(); 2147896Sache vr = not(); 2151590Srgrimes vl = vl && vr; 2161590Srgrimes } 2171590Srgrimes 2181590Srgrimes ungetch(); 2191590Srgrimes return vl; 2201590Srgrimes} 2211590Srgrimes 2221590Srgrimes/* 2237896Sache * not : eqrel | '!' not 2241590Srgrimes */ 2251590Srgrimesstatic int 2267896Sachenot() 2271590Srgrimes{ 22895060Sjmallett int val, c; 2291590Srgrimes 2307896Sache if ((c = skipws()) == '!' && getch() != '=') { 2311590Srgrimes ungetch(); 2327896Sache val = not(); 2337896Sache return !val; 2341590Srgrimes } 2351590Srgrimes 2367896Sache if (c == '!') 2371590Srgrimes ungetch(); 2381590Srgrimes ungetch(); 2397896Sache return eqrel(); 2401590Srgrimes} 2411590Srgrimes 2421590Srgrimes/* 2437896Sache * eqrel : shift { eqrelop shift } 2441590Srgrimes */ 2451590Srgrimesstatic int 2467896Sacheeqrel() 2471590Srgrimes{ 24895060Sjmallett int vl, vr, eqrel; 2491590Srgrimes 2507896Sache vl = shift(); 2517896Sache while ((eqrel = geteqrel()) != -1) { 2527896Sache vr = shift(); 2531590Srgrimes 2547896Sache switch (eqrel) { 2551590Srgrimes 2561590Srgrimes case EQL: 2571590Srgrimes vl = (vl == vr); 2581590Srgrimes break; 2591590Srgrimes case NEQ: 2601590Srgrimes vl = (vl != vr); 2611590Srgrimes break; 2621590Srgrimes 2631590Srgrimes case LEQ: 2641590Srgrimes vl = (vl <= vr); 2651590Srgrimes break; 2661590Srgrimes case LSS: 2671590Srgrimes vl = (vl < vr); 2681590Srgrimes break; 2691590Srgrimes case GTR: 2701590Srgrimes vl = (vl > vr); 2711590Srgrimes break; 2721590Srgrimes case GEQ: 2731590Srgrimes vl = (vl >= vr); 2741590Srgrimes break; 2751590Srgrimes } 2761590Srgrimes } 2771590Srgrimes return vl; 2781590Srgrimes} 2791590Srgrimes 2801590Srgrimes/* 2811590Srgrimes * shift : primary { shop primary } 2821590Srgrimes */ 2831590Srgrimesstatic int 2841590Srgrimesshift() 2851590Srgrimes{ 28695060Sjmallett int vl, vr, c; 2871590Srgrimes 2881590Srgrimes vl = primary(); 2897896Sache while (((c = skipws()) == '<' || c == '>') && getch() == c) { 2901590Srgrimes vr = primary(); 2911590Srgrimes 2921590Srgrimes if (c == '<') 2931590Srgrimes vl <<= vr; 2941590Srgrimes else 2951590Srgrimes vl >>= vr; 2961590Srgrimes } 2971590Srgrimes 2981590Srgrimes if (c == '<' || c == '>') 2991590Srgrimes ungetch(); 3001590Srgrimes ungetch(); 3011590Srgrimes return vl; 3021590Srgrimes} 3031590Srgrimes 3041590Srgrimes/* 3051590Srgrimes * primary : term { addop term } 3061590Srgrimes */ 3071590Srgrimesstatic int 3081590Srgrimesprimary() 3091590Srgrimes{ 31095060Sjmallett int c, vl, vr; 3111590Srgrimes 3121590Srgrimes vl = term(); 3131590Srgrimes while ((c = skipws()) == '+' || c == '-') { 3141590Srgrimes vr = term(); 3157896Sache 3161590Srgrimes if (c == '+') 3171590Srgrimes vl += vr; 3181590Srgrimes else 3191590Srgrimes vl -= vr; 3201590Srgrimes } 3211590Srgrimes 3221590Srgrimes ungetch(); 3231590Srgrimes return vl; 3241590Srgrimes} 3251590Srgrimes 3261590Srgrimes/* 3277896Sache * <term> := <exp> { <mulop> <exp> } 3281590Srgrimes */ 3291590Srgrimesstatic int 3301590Srgrimesterm() 3311590Srgrimes{ 33295060Sjmallett int c, vl, vr; 3331590Srgrimes 3347896Sache vl = exp(); 3351590Srgrimes while ((c = skipws()) == '*' || c == '/' || c == '%') { 3367896Sache vr = exp(); 3371590Srgrimes 3381590Srgrimes switch (c) { 3391590Srgrimes case '*': 3401590Srgrimes vl *= vr; 3411590Srgrimes break; 3421590Srgrimes case '/': 34395060Sjmallett if (vr == 0) 34495060Sjmallett errx(1, "division by zero in eval."); 34595060Sjmallett else 34695060Sjmallett vl /= vr; 3471590Srgrimes break; 3481590Srgrimes case '%': 34995060Sjmallett if (vr == 0) 35095060Sjmallett errx(1, "modulo zero in eval."); 35195060Sjmallett else 35295060Sjmallett vl %= vr; 3531590Srgrimes break; 3541590Srgrimes } 3551590Srgrimes } 3561590Srgrimes ungetch(); 3571590Srgrimes return vl; 3581590Srgrimes} 3591590Srgrimes 3601590Srgrimes/* 3617896Sache * <term> := <unary> { <expop> <unary> } 3627896Sache */ 3637896Sachestatic int 3647896Sacheexp() 3657896Sache{ 36695060Sjmallett int c, vl, vr, n; 3677896Sache 3687896Sache vl = unary(); 3697896Sache switch (c = skipws()) { 3707896Sache 3717896Sache case '*': 3727896Sache if (getch() != '*') { 3737896Sache ungetch(); 3747896Sache break; 3757896Sache } 3767896Sache 3777896Sache case '^': 3787896Sache vr = exp(); 3797896Sache n = 1; 3807896Sache while (vr-- > 0) 3817896Sache n *= vl; 3827896Sache return n; 3837896Sache } 3847896Sache 3857896Sache ungetch(); 3867896Sache return vl; 3877896Sache} 3887896Sache 3897896Sache/* 3901590Srgrimes * unary : factor | unop unary 3911590Srgrimes */ 3921590Srgrimesstatic int 3931590Srgrimesunary() 3941590Srgrimes{ 39595060Sjmallett int val, c; 3961590Srgrimes 3977896Sache if ((c = skipws()) == '+' || c == '-' || c == '~') { 3981590Srgrimes val = unary(); 3991590Srgrimes 4001590Srgrimes switch (c) { 4017896Sache case '+': 4027896Sache return val; 4037896Sache case '-': 4047896Sache return -val; 4051590Srgrimes case '~': 4061590Srgrimes return ~val; 4071590Srgrimes } 4081590Srgrimes } 4091590Srgrimes 4101590Srgrimes ungetch(); 4111590Srgrimes return factor(); 4121590Srgrimes} 4131590Srgrimes 4141590Srgrimes/* 4151590Srgrimes * factor : constant | '(' query ')' 4161590Srgrimes */ 4171590Srgrimesstatic int 4181590Srgrimesfactor() 4191590Srgrimes{ 42095060Sjmallett int val; 4211590Srgrimes 4221590Srgrimes if (skipws() == '(') { 4231590Srgrimes val = query(); 4241590Srgrimes if (skipws() != ')') 4251590Srgrimes experr("bad factor"); 4261590Srgrimes return val; 4271590Srgrimes } 4281590Srgrimes 4291590Srgrimes ungetch(); 4301590Srgrimes return constant(); 4311590Srgrimes} 4321590Srgrimes 4331590Srgrimes/* 4341590Srgrimes * constant: num | 'char' 4351590Srgrimes * Note: constant() handles multi-byte constants 4361590Srgrimes */ 4371590Srgrimesstatic int 4381590Srgrimesconstant() 4391590Srgrimes{ 44095060Sjmallett int i; 44195060Sjmallett int value; 44295060Sjmallett int c; 4431590Srgrimes int v[sizeof(int)]; 4441590Srgrimes 4451590Srgrimes if (skipws() != '\'') { 4461590Srgrimes ungetch(); 4471590Srgrimes return num(); 4481590Srgrimes } 4491590Srgrimes for (i = 0; i < sizeof(int); i++) { 4501590Srgrimes if ((c = getch()) == '\'') { 4511590Srgrimes ungetch(); 4521590Srgrimes break; 4531590Srgrimes } 4541590Srgrimes if (c == '\\') { 4551590Srgrimes switch (c = getch()) { 4561590Srgrimes case '0': 4571590Srgrimes case '1': 4581590Srgrimes case '2': 4591590Srgrimes case '3': 4601590Srgrimes case '4': 4611590Srgrimes case '5': 4621590Srgrimes case '6': 4631590Srgrimes case '7': 4641590Srgrimes ungetch(); 4651590Srgrimes c = num(); 4661590Srgrimes break; 4671590Srgrimes case 'n': 4681590Srgrimes c = 012; 4691590Srgrimes break; 4701590Srgrimes case 'r': 4711590Srgrimes c = 015; 4721590Srgrimes break; 4731590Srgrimes case 't': 4741590Srgrimes c = 011; 4751590Srgrimes break; 4761590Srgrimes case 'b': 4771590Srgrimes c = 010; 4781590Srgrimes break; 4791590Srgrimes case 'f': 4801590Srgrimes c = 014; 4811590Srgrimes break; 4821590Srgrimes } 4831590Srgrimes } 4841590Srgrimes v[i] = c; 4851590Srgrimes } 4861590Srgrimes if (i == 0 || getch() != '\'') 4871590Srgrimes experr("illegal character constant"); 4881590Srgrimes for (value = 0; --i >= 0;) { 4891590Srgrimes value <<= 8; 4901590Srgrimes value += v[i]; 4911590Srgrimes } 4921590Srgrimes return value; 4931590Srgrimes} 4941590Srgrimes 4951590Srgrimes/* 4961590Srgrimes * num : digit | num digit 4971590Srgrimes */ 4981590Srgrimesstatic int 4991590Srgrimesnum() 5001590Srgrimes{ 50195060Sjmallett int rval, c, base; 5021590Srgrimes int ndig; 5031590Srgrimes 5041590Srgrimes rval = 0; 5051590Srgrimes ndig = 0; 50695060Sjmallett c = skipws(); 50795060Sjmallett if (c == '0') { 50895060Sjmallett c = skipws(); 50995060Sjmallett if (c == 'x' || c == 'X') { 51095060Sjmallett base = HEX; 51195060Sjmallett c = skipws(); 51295060Sjmallett } else { 51395060Sjmallett base = OCTAL; 51495060Sjmallett ndig++; 51595060Sjmallett } 51695060Sjmallett } else 51795060Sjmallett base = DECIMAL; 51895060Sjmallett for(;;) { 51995060Sjmallett switch(c) { 52095060Sjmallett case '8': case '9': 52195060Sjmallett if (base == OCTAL) 52295060Sjmallett goto bad_digit; 52395060Sjmallett /*FALLTHRU*/ 52495060Sjmallett case '0': case '1': case '2': case '3': 52595060Sjmallett case '4': case '5': case '6': case '7': 52695060Sjmallett rval *= base; 52795060Sjmallett rval += c - '0'; 52895060Sjmallett break; 52995060Sjmallett case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 53095060Sjmallett c = tolower(c); 53195060Sjmallett case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 53295060Sjmallett if (base == HEX) { 53395060Sjmallett rval *= base; 53495060Sjmallett rval += c - 'a' + 10; 53595060Sjmallett break; 53695060Sjmallett } 53795060Sjmallett /*FALLTHRU*/ 53895060Sjmallett default: 53995060Sjmallett goto bad_digit; 54095060Sjmallett } 5411590Srgrimes c = getch(); 5421590Srgrimes ndig++; 5431590Srgrimes } 54495060Sjmallettbad_digit: 5451590Srgrimes ungetch(); 54695060Sjmallett 5471590Srgrimes if (ndig == 0) 5481590Srgrimes experr("bad constant"); 54995060Sjmallett 5501590Srgrimes return rval; 5511590Srgrimes} 5521590Srgrimes 5531590Srgrimes/* 5547896Sache * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>=' 5551590Srgrimes */ 5561590Srgrimesstatic int 5577896Sachegeteqrel() 5581590Srgrimes{ 55995060Sjmallett int c1, c2; 5601590Srgrimes 5611590Srgrimes c1 = skipws(); 5621590Srgrimes c2 = getch(); 5631590Srgrimes 5641590Srgrimes switch (c1) { 5651590Srgrimes 5661590Srgrimes case '=': 5671590Srgrimes if (c2 != '=') 5681590Srgrimes ungetch(); 5691590Srgrimes return EQL; 5701590Srgrimes 5711590Srgrimes case '!': 5721590Srgrimes if (c2 == '=') 5731590Srgrimes return NEQ; 5741590Srgrimes ungetch(); 5751590Srgrimes ungetch(); 5761590Srgrimes return -1; 5771590Srgrimes 5781590Srgrimes case '<': 5791590Srgrimes if (c2 == '=') 5801590Srgrimes return LEQ; 5811590Srgrimes ungetch(); 5821590Srgrimes return LSS; 5831590Srgrimes 5841590Srgrimes case '>': 5851590Srgrimes if (c2 == '=') 5861590Srgrimes return GEQ; 5871590Srgrimes ungetch(); 5881590Srgrimes return GTR; 5891590Srgrimes 5901590Srgrimes default: 5911590Srgrimes ungetch(); 5921590Srgrimes ungetch(); 5931590Srgrimes return -1; 5941590Srgrimes } 5951590Srgrimes} 5961590Srgrimes 5971590Srgrimes/* 5981590Srgrimes * Skip over any white space and return terminating char. 5991590Srgrimes */ 6001590Srgrimesstatic int 6011590Srgrimesskipws() 6021590Srgrimes{ 60395060Sjmallett int c; 6041590Srgrimes 6051590Srgrimes while ((c = getch()) <= ' ' && c > EOS) 6061590Srgrimes ; 6071590Srgrimes return c; 6081590Srgrimes} 6091590Srgrimes 6101590Srgrimes/* 61195060Sjmallett * resets environment to eval(), prints an error 6121590Srgrimes * and forces eval to return FALSE. 6131590Srgrimes */ 6141590Srgrimesstatic void 6151590Srgrimesexperr(msg) 61695060Sjmallett const char *msg; 6171590Srgrimes{ 61895060Sjmallett printf("m4: %s in expr %s.\n", msg, where); 6191590Srgrimes longjmp(expjump, -1); 6201590Srgrimes} 621