1*9679Slinton /* Copyright (c) 1982 Regents of the University of California */ 2*9679Slinton 3*9679Slinton static char sccsid[] = "@(#)@(#)scanner.c 1.1 12/15/82"; 4*9679Slinton 5*9679Slinton /* 6*9679Slinton * Debugger scanner. 7*9679Slinton */ 8*9679Slinton 9*9679Slinton #include "defs.h" 10*9679Slinton #include "scanner.h" 11*9679Slinton #include "main.h" 12*9679Slinton #include "keywords.h" 13*9679Slinton #include "tree.h" 14*9679Slinton #include "symbols.h" 15*9679Slinton #include "names.h" 16*9679Slinton #include "y.tab.h" 17*9679Slinton 18*9679Slinton #ifndef public 19*9679Slinton typedef int Token; 20*9679Slinton #endif 21*9679Slinton 22*9679Slinton public String initfile = ".dbxinit"; 23*9679Slinton 24*9679Slinton typedef enum { WHITE, ALPHA, NUM, OTHER } Charclass; 25*9679Slinton 26*9679Slinton private Charclass class[256 + 1]; 27*9679Slinton private Charclass *lexclass = class + 1; 28*9679Slinton 29*9679Slinton #define isdigit(c) (lexclass[c] == NUM) 30*9679Slinton #define isalnum(c) (lexclass[c] == ALPHA or lexclass[c] == NUM) 31*9679Slinton #define ishexdigit(c) ( \ 32*9679Slinton isdigit(c) or (c >= 'a' and c <= 'f') or (c >= 'A' and c <= 'F') \ 33*9679Slinton ) 34*9679Slinton 35*9679Slinton #define MAXLINESIZE 1024 36*9679Slinton 37*9679Slinton private File in; 38*9679Slinton private Char linebuf[MAXLINESIZE]; 39*9679Slinton private Char *curchar; 40*9679Slinton 41*9679Slinton #define MAXINCLDEPTH 10 42*9679Slinton 43*9679Slinton private struct { 44*9679Slinton File savefile; 45*9679Slinton Filename savefn; 46*9679Slinton int savelineno; 47*9679Slinton } inclinfo[MAXINCLDEPTH]; 48*9679Slinton 49*9679Slinton private unsigned int curinclindex; 50*9679Slinton 51*9679Slinton private Boolean firsttoken = true; 52*9679Slinton private Boolean firstinit = true; 53*9679Slinton 54*9679Slinton private Token getident(); 55*9679Slinton private Token getnum(); 56*9679Slinton private Token getstring(); 57*9679Slinton private Boolean eofinput(); 58*9679Slinton private Char charcon(); 59*9679Slinton private Char charlookup(); 60*9679Slinton 61*9679Slinton private enterlexclass(class, s) 62*9679Slinton Charclass class; 63*9679Slinton String s; 64*9679Slinton { 65*9679Slinton register char *p; 66*9679Slinton 67*9679Slinton for (p = s; *p != '\0'; p++) { 68*9679Slinton lexclass[*p] = class; 69*9679Slinton } 70*9679Slinton } 71*9679Slinton 72*9679Slinton public scanner_init() 73*9679Slinton { 74*9679Slinton register Integer i; 75*9679Slinton 76*9679Slinton for (i = 0; i < 257; i++) { 77*9679Slinton class[i] = OTHER; 78*9679Slinton } 79*9679Slinton enterlexclass(WHITE, " \t"); 80*9679Slinton enterlexclass(ALPHA, "abcdefghijklmnopqrstuvwxyz"); 81*9679Slinton enterlexclass(ALPHA, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_$"); 82*9679Slinton enterlexclass(NUM, "0123456789"); 83*9679Slinton in = stdin; 84*9679Slinton errfilename = nil; 85*9679Slinton errlineno = 0; 86*9679Slinton curchar = linebuf; 87*9679Slinton linebuf[0] = '\0'; 88*9679Slinton if (runfirst) { 89*9679Slinton firstinit = false; 90*9679Slinton firsttoken = false; 91*9679Slinton } else if (firstinit and isterm(in)) { 92*9679Slinton firstinit = false; 93*9679Slinton printf("> "); 94*9679Slinton fflush(stdout); 95*9679Slinton } 96*9679Slinton } 97*9679Slinton 98*9679Slinton /* 99*9679Slinton * Read a single token. 100*9679Slinton * 101*9679Slinton * Input is line buffered. 102*9679Slinton * 103*9679Slinton * There are two "modes" of operation: one as in a compiler, 104*9679Slinton * and one for reading shell-like syntax. 105*9679Slinton */ 106*9679Slinton 107*9679Slinton private Boolean shellmode; 108*9679Slinton 109*9679Slinton public Token yylex() 110*9679Slinton { 111*9679Slinton register int c; 112*9679Slinton register char *p; 113*9679Slinton register Token t; 114*9679Slinton String line; 115*9679Slinton 116*9679Slinton p = curchar; 117*9679Slinton if (*p == '\0') { 118*9679Slinton do { 119*9679Slinton if (isterm(in)) { 120*9679Slinton if (firsttoken) { 121*9679Slinton firsttoken = false; 122*9679Slinton } else { 123*9679Slinton printf("> "); 124*9679Slinton fflush(stdout); 125*9679Slinton } 126*9679Slinton } 127*9679Slinton line = fgets(linebuf, MAXLINESIZE, in); 128*9679Slinton } while (line == nil and not eofinput()); 129*9679Slinton if (line == nil) { 130*9679Slinton c = EOF; 131*9679Slinton } else { 132*9679Slinton p = linebuf; 133*9679Slinton while (lexclass[*p] == WHITE) { 134*9679Slinton p++; 135*9679Slinton } 136*9679Slinton shellmode = false; 137*9679Slinton } 138*9679Slinton } else { 139*9679Slinton while (lexclass[*p] == WHITE) { 140*9679Slinton p++; 141*9679Slinton } 142*9679Slinton } 143*9679Slinton curchar = p; 144*9679Slinton c = *p; 145*9679Slinton if (lexclass[c] == ALPHA) { 146*9679Slinton t = getident(); 147*9679Slinton } else if (lexclass[c] == NUM) { 148*9679Slinton t = getnum(); 149*9679Slinton } else { 150*9679Slinton ++curchar; 151*9679Slinton switch (c) { 152*9679Slinton case '\n': 153*9679Slinton t = '\n'; 154*9679Slinton if (errlineno != 0) { 155*9679Slinton errlineno++; 156*9679Slinton } 157*9679Slinton break; 158*9679Slinton 159*9679Slinton case '"': 160*9679Slinton case '\'': 161*9679Slinton t = getstring(); 162*9679Slinton break; 163*9679Slinton 164*9679Slinton case '.': 165*9679Slinton if (shellmode) { 166*9679Slinton --curchar; 167*9679Slinton t = getident(); 168*9679Slinton } else if (isdigit(*curchar)) { 169*9679Slinton --curchar; 170*9679Slinton t = getnum(); 171*9679Slinton } else { 172*9679Slinton t = '.'; 173*9679Slinton } 174*9679Slinton break; 175*9679Slinton 176*9679Slinton case '<': 177*9679Slinton if (not shellmode and *curchar == '<') { 178*9679Slinton ++curchar; 179*9679Slinton t = LFORMER; 180*9679Slinton } else { 181*9679Slinton t = '<'; 182*9679Slinton } 183*9679Slinton break; 184*9679Slinton 185*9679Slinton case '>': 186*9679Slinton if (not shellmode and *curchar == '>') { 187*9679Slinton ++curchar; 188*9679Slinton t = RFORMER; 189*9679Slinton } else { 190*9679Slinton t = '>'; 191*9679Slinton } 192*9679Slinton break; 193*9679Slinton 194*9679Slinton case '#': 195*9679Slinton if (*curchar == '^') { 196*9679Slinton ++curchar; 197*9679Slinton t = ABSTRACTION; 198*9679Slinton } else { 199*9679Slinton t = '#'; 200*9679Slinton } 201*9679Slinton break; 202*9679Slinton 203*9679Slinton case '-': 204*9679Slinton if (shellmode) { 205*9679Slinton --curchar; 206*9679Slinton t = getident(); 207*9679Slinton } else if (*curchar == '>') { 208*9679Slinton ++curchar; 209*9679Slinton t = ARROW; 210*9679Slinton } else { 211*9679Slinton t = '-'; 212*9679Slinton } 213*9679Slinton break; 214*9679Slinton 215*9679Slinton case EOF: 216*9679Slinton t = 0; 217*9679Slinton break; 218*9679Slinton 219*9679Slinton default: 220*9679Slinton if (shellmode and index("!&*()[]", c) == nil) { 221*9679Slinton --curchar; 222*9679Slinton t = getident(); 223*9679Slinton } else { 224*9679Slinton t = c; 225*9679Slinton } 226*9679Slinton break; 227*9679Slinton } 228*9679Slinton } 229*9679Slinton # ifdef LEXDEBUG 230*9679Slinton if (lexdebug) { 231*9679Slinton fprintf(stderr, "yylex returns "); 232*9679Slinton print_token(stderr, t); 233*9679Slinton fprintf(stderr, "\n"); 234*9679Slinton } 235*9679Slinton # endif 236*9679Slinton return t; 237*9679Slinton } 238*9679Slinton 239*9679Slinton /* 240*9679Slinton * Parser error handling. 241*9679Slinton */ 242*9679Slinton 243*9679Slinton public yyerror(s) 244*9679Slinton String s; 245*9679Slinton { 246*9679Slinton register Char *p, *tokenbegin, *tokenend; 247*9679Slinton register Integer len; 248*9679Slinton 249*9679Slinton if (streq(s, "syntax error")) { 250*9679Slinton beginerrmsg(); 251*9679Slinton tokenend = curchar - 1; 252*9679Slinton tokenbegin = tokenend; 253*9679Slinton while (lexclass[*tokenbegin] != WHITE and tokenbegin > &linebuf[0]) { 254*9679Slinton --tokenbegin; 255*9679Slinton } 256*9679Slinton len = tokenend - tokenbegin + 1; 257*9679Slinton p = tokenbegin; 258*9679Slinton if (p > &linebuf[0]) { 259*9679Slinton while (lexclass[*p] == WHITE and p > &linebuf[0]) { 260*9679Slinton --p; 261*9679Slinton } 262*9679Slinton } 263*9679Slinton if (p == &linebuf[0]) { 264*9679Slinton fprintf(stderr, "unrecognized command \"%.*s\"", len, tokenbegin); 265*9679Slinton } else { 266*9679Slinton fprintf(stderr, "syntax error"); 267*9679Slinton if (len != 0) { 268*9679Slinton fprintf(stderr, " on \"%.*s\"", len, tokenbegin); 269*9679Slinton } 270*9679Slinton } 271*9679Slinton enderrmsg(); 272*9679Slinton } else { 273*9679Slinton error(s); 274*9679Slinton } 275*9679Slinton } 276*9679Slinton 277*9679Slinton /* 278*9679Slinton * Eat the current line. 279*9679Slinton */ 280*9679Slinton 281*9679Slinton public gobble() 282*9679Slinton { 283*9679Slinton curchar = linebuf; 284*9679Slinton linebuf[0] = '\0'; 285*9679Slinton } 286*9679Slinton 287*9679Slinton /* 288*9679Slinton * Scan an identifier and check to see if it's a keyword. 289*9679Slinton */ 290*9679Slinton 291*9679Slinton private Token getident() 292*9679Slinton { 293*9679Slinton char buf[256]; 294*9679Slinton register Char *p, *q; 295*9679Slinton register Token t; 296*9679Slinton 297*9679Slinton p = curchar; 298*9679Slinton q = buf; 299*9679Slinton if (shellmode) { 300*9679Slinton do { 301*9679Slinton *q++ = *p++; 302*9679Slinton } while (index(" \t\n!&<>*[]()", *p) == nil); 303*9679Slinton } else { 304*9679Slinton do { 305*9679Slinton *q++ = *p++; 306*9679Slinton } while (isalnum(*p)); 307*9679Slinton } 308*9679Slinton curchar = p; 309*9679Slinton *q = '\0'; 310*9679Slinton yylval.y_name = identname(buf, false); 311*9679Slinton if (not shellmode) { 312*9679Slinton t = findkeyword(yylval.y_name); 313*9679Slinton if (t == nil) { 314*9679Slinton t = NAME; 315*9679Slinton } 316*9679Slinton } else { 317*9679Slinton t = NAME; 318*9679Slinton } 319*9679Slinton return t; 320*9679Slinton } 321*9679Slinton 322*9679Slinton /* 323*9679Slinton * Scan a number. 324*9679Slinton */ 325*9679Slinton 326*9679Slinton private Token getnum() 327*9679Slinton { 328*9679Slinton char buf[256]; 329*9679Slinton register Char *p, *q; 330*9679Slinton register Token t; 331*9679Slinton Integer base; 332*9679Slinton 333*9679Slinton p = curchar; 334*9679Slinton q = buf; 335*9679Slinton if (*p == '0') { 336*9679Slinton if (*(p+1) == 'x') { 337*9679Slinton p += 2; 338*9679Slinton base = 16; 339*9679Slinton } else { 340*9679Slinton base = 8; 341*9679Slinton } 342*9679Slinton } else { 343*9679Slinton base = 10; 344*9679Slinton } 345*9679Slinton if (base == 16) { 346*9679Slinton do { 347*9679Slinton *q++ = *p++; 348*9679Slinton } while (ishexdigit(*p)); 349*9679Slinton } else { 350*9679Slinton do { 351*9679Slinton *q++ = *p++; 352*9679Slinton } while (isdigit(*p)); 353*9679Slinton } 354*9679Slinton if (*p == '.') { 355*9679Slinton do { 356*9679Slinton *q++ = *p++; 357*9679Slinton } while (isdigit(*p)); 358*9679Slinton if (*p == 'e' or *p == 'E') { 359*9679Slinton p++; 360*9679Slinton if (*p == '+' or *p == '-' or isdigit(*p)) { 361*9679Slinton *q++ = 'e'; 362*9679Slinton do { 363*9679Slinton *q++ = *p++; 364*9679Slinton } while (isdigit(*p)); 365*9679Slinton } 366*9679Slinton } 367*9679Slinton *q = '\0'; 368*9679Slinton yylval.y_real = atof(buf); 369*9679Slinton t = REAL; 370*9679Slinton } else { 371*9679Slinton *q = '\0'; 372*9679Slinton switch (base) { 373*9679Slinton case 10: 374*9679Slinton yylval.y_int = atol(buf); 375*9679Slinton break; 376*9679Slinton 377*9679Slinton case 8: 378*9679Slinton yylval.y_int = octal(buf); 379*9679Slinton break; 380*9679Slinton 381*9679Slinton case 16: 382*9679Slinton yylval.y_int = hex(buf); 383*9679Slinton break; 384*9679Slinton 385*9679Slinton default: 386*9679Slinton badcaseval(base); 387*9679Slinton } 388*9679Slinton t = INT; 389*9679Slinton } 390*9679Slinton curchar = p; 391*9679Slinton return t; 392*9679Slinton } 393*9679Slinton 394*9679Slinton /* 395*9679Slinton * Convert a string of octal digits to an integer. 396*9679Slinton */ 397*9679Slinton 398*9679Slinton private int octal(s) 399*9679Slinton String s; 400*9679Slinton { 401*9679Slinton register Char *p; 402*9679Slinton register Integer n; 403*9679Slinton 404*9679Slinton n = 0; 405*9679Slinton for (p = s; *p != '\0'; p++) { 406*9679Slinton n = 8*n + (*p - '0'); 407*9679Slinton } 408*9679Slinton return n; 409*9679Slinton } 410*9679Slinton 411*9679Slinton /* 412*9679Slinton * Convert a string of hexadecimal digits to an integer. 413*9679Slinton */ 414*9679Slinton 415*9679Slinton private int hex(s) 416*9679Slinton String s; 417*9679Slinton { 418*9679Slinton register Char *p; 419*9679Slinton register Integer n; 420*9679Slinton 421*9679Slinton n = 0; 422*9679Slinton for (p = s; *p != '\0'; p++) { 423*9679Slinton n *= 16; 424*9679Slinton if (*p >= 'a' and *p <= 'f') { 425*9679Slinton n += (*p - 'a' + 10); 426*9679Slinton } else if (*p >= 'A' and *p <= 'F') { 427*9679Slinton n += (*p - 'A' + 10); 428*9679Slinton } else { 429*9679Slinton n += (*p - '0'); 430*9679Slinton } 431*9679Slinton } 432*9679Slinton return n; 433*9679Slinton } 434*9679Slinton 435*9679Slinton /* 436*9679Slinton * Scan a string. 437*9679Slinton */ 438*9679Slinton 439*9679Slinton private Token getstring() 440*9679Slinton { 441*9679Slinton char buf[256]; 442*9679Slinton register Char *p, *q; 443*9679Slinton Boolean endofstring; 444*9679Slinton 445*9679Slinton p = curchar; 446*9679Slinton q = buf; 447*9679Slinton endofstring = false; 448*9679Slinton while (not endofstring) { 449*9679Slinton if (*p == '\n' or *p == '\0') { 450*9679Slinton error("non-terminated string"); 451*9679Slinton endofstring = true; 452*9679Slinton } else if (*p == '"') { 453*9679Slinton if (*(p+1) != '"') { 454*9679Slinton endofstring = true; 455*9679Slinton } else { 456*9679Slinton *q++ = *p; 457*9679Slinton } 458*9679Slinton } else { 459*9679Slinton *q++ = charcon(*p); 460*9679Slinton } 461*9679Slinton p++; 462*9679Slinton } 463*9679Slinton curchar = p; 464*9679Slinton *q = '\0'; 465*9679Slinton yylval.y_string = strdup(buf); 466*9679Slinton return STRING; 467*9679Slinton } 468*9679Slinton 469*9679Slinton /* 470*9679Slinton * Process a character constant. 471*9679Slinton * Watch out for backslashes. 472*9679Slinton */ 473*9679Slinton 474*9679Slinton private Char charcon(ch) 475*9679Slinton Char ch; 476*9679Slinton { 477*9679Slinton Char c, buf[10], *p, *q; 478*9679Slinton 479*9679Slinton p = curchar; 480*9679Slinton if (ch == '\\') { 481*9679Slinton if (*p != '\\') { 482*9679Slinton q = buf; 483*9679Slinton do { 484*9679Slinton *q++ = *p++; 485*9679Slinton } while (*p != '\\' and *p != '\n' and *p != '\0'); 486*9679Slinton if (*p != '\\') { 487*9679Slinton ungetc(*p, in); 488*9679Slinton error("non-terminated character constant"); 489*9679Slinton } 490*9679Slinton *q = '\0'; 491*9679Slinton if (isdigit(buf[0])) { 492*9679Slinton c = (Char) octal(buf); 493*9679Slinton } else { 494*9679Slinton c = charlookup(buf); 495*9679Slinton } 496*9679Slinton curchar = p; 497*9679Slinton } else { 498*9679Slinton c = '\\'; 499*9679Slinton } 500*9679Slinton } else { 501*9679Slinton c = ch; 502*9679Slinton } 503*9679Slinton return c; 504*9679Slinton } 505*9679Slinton 506*9679Slinton /* 507*9679Slinton * Do a lookup for a ASCII character name. 508*9679Slinton */ 509*9679Slinton 510*9679Slinton private String ascii[] = { 511*9679Slinton "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", 512*9679Slinton "BS", "HT", "NL", "VT", "NP", "CR", "SO", "SI", 513*9679Slinton "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", 514*9679Slinton "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US", 515*9679Slinton "SP", nil 516*9679Slinton }; 517*9679Slinton 518*9679Slinton private char charlookup(s) 519*9679Slinton String s; 520*9679Slinton { 521*9679Slinton register int i; 522*9679Slinton 523*9679Slinton for (i = 0; ascii[i] != NULL; i++) { 524*9679Slinton if (streq(s, ascii[i])) { 525*9679Slinton return i; 526*9679Slinton } 527*9679Slinton } 528*9679Slinton if (streq(s, "DEL")) { 529*9679Slinton return 0177; 530*9679Slinton } 531*9679Slinton error("unknown ascii name \"%s\"", s); 532*9679Slinton return '?'; 533*9679Slinton } 534*9679Slinton 535*9679Slinton /* 536*9679Slinton * Input file management routines. 537*9679Slinton */ 538*9679Slinton 539*9679Slinton public setinput(filename) 540*9679Slinton Filename filename; 541*9679Slinton { 542*9679Slinton File f; 543*9679Slinton 544*9679Slinton f = fopen(filename, "r"); 545*9679Slinton if (f == nil) { 546*9679Slinton error("can't open %s", filename); 547*9679Slinton } else { 548*9679Slinton if (curinclindex >= MAXINCLDEPTH) { 549*9679Slinton error("unreasonable input nesting on \"%s\"", filename); 550*9679Slinton } 551*9679Slinton inclinfo[curinclindex].savefile = in; 552*9679Slinton inclinfo[curinclindex].savefn = errfilename; 553*9679Slinton inclinfo[curinclindex].savelineno = errlineno; 554*9679Slinton curinclindex++; 555*9679Slinton in = f; 556*9679Slinton errfilename = filename; 557*9679Slinton errlineno = 1; 558*9679Slinton } 559*9679Slinton } 560*9679Slinton 561*9679Slinton private Boolean eofinput() 562*9679Slinton { 563*9679Slinton register Boolean b; 564*9679Slinton 565*9679Slinton if (curinclindex == 0) { 566*9679Slinton if (isterm(in)) { 567*9679Slinton putchar('\n'); 568*9679Slinton b = false; 569*9679Slinton } else { 570*9679Slinton b = true; 571*9679Slinton } 572*9679Slinton } else { 573*9679Slinton fclose(in); 574*9679Slinton --curinclindex; 575*9679Slinton in = inclinfo[curinclindex].savefile; 576*9679Slinton errfilename = inclinfo[curinclindex].savefn; 577*9679Slinton errlineno = inclinfo[curinclindex].savelineno; 578*9679Slinton b = false; 579*9679Slinton } 580*9679Slinton return b; 581*9679Slinton } 582*9679Slinton 583*9679Slinton /* 584*9679Slinton * Pop the current input. Return whether successful. 585*9679Slinton */ 586*9679Slinton 587*9679Slinton public Boolean popinput() 588*9679Slinton { 589*9679Slinton Boolean b; 590*9679Slinton 591*9679Slinton if (curinclindex == 0) { 592*9679Slinton b = false; 593*9679Slinton } else { 594*9679Slinton b = (Boolean) (not eofinput()); 595*9679Slinton } 596*9679Slinton return b; 597*9679Slinton } 598*9679Slinton 599*9679Slinton /* 600*9679Slinton * Return whether we are currently reading from standard input. 601*9679Slinton */ 602*9679Slinton 603*9679Slinton public Boolean isstdin() 604*9679Slinton { 605*9679Slinton return (Boolean) (in == stdin); 606*9679Slinton } 607*9679Slinton 608*9679Slinton /* 609*9679Slinton * Send the current line to the shell. 610*9679Slinton */ 611*9679Slinton 612*9679Slinton public shellline() 613*9679Slinton { 614*9679Slinton register char *p; 615*9679Slinton 616*9679Slinton p = curchar; 617*9679Slinton while (*p != '\0' and (*p == '\n' or lexclass[*p] == WHITE)) { 618*9679Slinton ++p; 619*9679Slinton } 620*9679Slinton shell(p); 621*9679Slinton if (*p == '\0' and isterm(in)) { 622*9679Slinton putchar('\n'); 623*9679Slinton } 624*9679Slinton erecover(); 625*9679Slinton } 626*9679Slinton 627*9679Slinton /* 628*9679Slinton * Read the rest of the current line in "shell mode". 629*9679Slinton */ 630*9679Slinton 631*9679Slinton public beginshellmode() 632*9679Slinton { 633*9679Slinton shellmode = true; 634*9679Slinton } 635*9679Slinton 636*9679Slinton /* 637*9679Slinton * Print out a token for debugging. 638*9679Slinton */ 639*9679Slinton 640*9679Slinton public print_token(f, t) 641*9679Slinton File f; 642*9679Slinton Token t; 643*9679Slinton { 644*9679Slinton if (t == '\n') { 645*9679Slinton fprintf(f, "char '\\n'"); 646*9679Slinton } else if (t == EOF) { 647*9679Slinton fprintf(f, "EOF"); 648*9679Slinton } else if (t < 256) { 649*9679Slinton fprintf(f, "char '%c'", t); 650*9679Slinton } else { 651*9679Slinton fprintf(f, "\"%s\"", keywdstring(t)); 652*9679Slinton } 653*9679Slinton } 654