scat.c (30145B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <draw.h> 5 #include <event.h> 6 #include "sky.h" 7 #include "strings.c" 8 9 enum 10 { 11 NNGC=7840, /* number of NGC numbers [1..NNGC] */ 12 NIC = 5386, /* number of IC numbers */ 13 NNGCrec=NNGC+NIC, /* number of records in the NGC catalog (including IC's, starting at NNGC */ 14 NMrec=122, /* number of M records */ 15 NM=110, /* number of M numbers */ 16 NAbell=2712, /* number of records in the Abell catalog */ 17 NName=1000, /* number of prose names; estimated maximum (read from editable text file) */ 18 NBayer=1517, /* number of bayer entries */ 19 NSAO=258998, /* number of SAO stars */ 20 MAXcon=1932, /* maximum number of patches in a constellation */ 21 Ncon=88, /* number of constellations */ 22 Npatch=92053, /* highest patch number */ 23 }; 24 25 char ngctype[NNGCrec]; 26 Mindexrec mindex[NMrec]; 27 Namerec name[NName]; 28 Bayerec bayer[NBayer]; 29 int32 con[MAXcon]; 30 ushort conindex[Ncon+1]; 31 int32 patchaddr[Npatch+1]; 32 33 Record *rec; 34 Record *orec; 35 Record *cur; 36 37 char *dir; 38 int saodb; 39 int ngcdb; 40 int abelldb; 41 int ngctypedb; 42 int mindexdb; 43 int namedb; 44 int bayerdb; 45 int condb; 46 int conindexdb; 47 int patchdb; 48 char parsed[3]; 49 int32 nrec; 50 int32 nreca; 51 int32 norec; 52 int32 noreca; 53 54 Biobuf bin; 55 Biobuf bout; 56 57 void 58 main(int argc, char *argv[]) 59 { 60 char *line; 61 62 dir = unsharp(DIR); 63 Binit(&bin, 0, OREAD); 64 Binit(&bout, 1, OWRITE); 65 if(argc != 1) 66 dir = argv[1]; 67 astro("", 1); 68 while(line = Brdline(&bin, '\n')){ 69 line[Blinelen(&bin)-1] = 0; 70 lookup(line, 1); 71 Bflush(&bout); 72 } 73 if(display != nil){ 74 closedisplay(display); 75 /* automatic refresh of rio window is triggered by mouse */ 76 /* close(open("/dev/mouse", OREAD)); */ 77 } 78 return; 79 } 80 81 void 82 reset(void) 83 { 84 nrec = 0; 85 cur = rec; 86 } 87 88 void 89 grow(void) 90 { 91 nrec++; 92 if(nreca < nrec){ 93 nreca = nrec+50; 94 rec = realloc(rec, nreca*sizeof(Record)); 95 if(rec == 0){ 96 fprint(2, "scat: realloc fails\n"); 97 exits("realloc"); 98 } 99 } 100 cur = rec+nrec-1; 101 } 102 103 void 104 copy(void) 105 { 106 if(noreca < nreca){ 107 noreca = nreca; 108 orec = realloc(orec, nreca*sizeof(Record)); 109 if(orec == 0){ 110 fprint(2, "scat: realloc fails\n"); 111 exits("realloc"); 112 } 113 } 114 memmove(orec, rec, nrec*sizeof(Record)); 115 norec = nrec; 116 } 117 118 int 119 eopen(char *s) 120 { 121 char buf[128]; 122 int f; 123 124 sprint(buf, "%s/%s.scat", dir, s); 125 f = open(buf, 0); 126 if(f<0){ 127 fprint(2, "scat: can't open %s\n", buf); 128 exits("open"); 129 } 130 return f; 131 } 132 133 134 void 135 Eread(int f, char *name, void *addr, int32 n) 136 { 137 if(read(f, addr, n) != n){ /* BUG! */ 138 fprint(2, "scat: read error on %s\n", name); 139 exits("read"); 140 } 141 } 142 143 char* 144 skipbl(char *s) 145 { 146 while(*s!=0 && (*s==' ' || *s=='\t')) 147 s++; 148 return s; 149 } 150 151 char* 152 skipstr(char *s, char *t) 153 { 154 while(*s && *s==*t) 155 s++, t++; 156 return skipbl(s); 157 } 158 159 /* produce little-endian int32 at address l */ 160 int32 161 Long(int32 *l) 162 { 163 uchar *p; 164 165 p = (uchar*)l; 166 return (int32)p[0]|((int32)p[1]<<8)|((int32)p[2]<<16)|((int32)p[3]<<24); 167 } 168 169 /* produce little-endian int32 at address l */ 170 int 171 Short(short *s) 172 { 173 uchar *p; 174 175 p = (uchar*)s; 176 return p[0]|(p[1]<<8); 177 } 178 179 void 180 nameopen(void) 181 { 182 Biobuf b; 183 int i; 184 char *l, *p; 185 186 if(namedb == 0){ 187 namedb = eopen("name"); 188 Binit(&b, namedb, OREAD); 189 for(i=0; i<NName; i++){ 190 l = Brdline(&b, '\n'); 191 if(l == 0) 192 break; 193 p = strchr(l, '\t'); 194 if(p == 0){ 195 Badformat: 196 Bprint(&bout, "warning: name.scat bad format; line %d\n", i+1); 197 break; 198 } 199 *p++ = 0; 200 strcpy(name[i].name, l); 201 if(strncmp(p, "ngc", 3) == 0) 202 name[i].ngc = atoi(p+3); 203 else if(strncmp(p, "ic", 2) == 0) 204 name[i].ngc = atoi(p+2)+NNGC; 205 else if(strncmp(p, "sao", 3) == 0) 206 name[i].sao = atoi(p+3); 207 else if(strncmp(p, "abell", 5) == 0) 208 name[i].abell = atoi(p+5); 209 else 210 goto Badformat; 211 } 212 if(i == NName) 213 Bprint(&bout, "warning: too many names in name.scat (max %d); extra ignored\n", NName); 214 close(namedb); 215 216 bayerdb = eopen("bayer"); 217 Eread(bayerdb, "bayer", bayer, sizeof bayer); 218 close(bayerdb); 219 for(i=0; i<NBayer; i++) 220 bayer[i].sao = Long(&bayer[i].sao); 221 } 222 } 223 224 void 225 saoopen(void) 226 { 227 if(saodb == 0){ 228 nameopen(); 229 saodb = eopen("sao"); 230 } 231 } 232 233 void 234 ngcopen(void) 235 { 236 if(ngcdb == 0){ 237 nameopen(); 238 ngcdb = eopen("ngc2000"); 239 ngctypedb = eopen("ngc2000type"); 240 Eread(ngctypedb, "ngctype", ngctype, sizeof ngctype); 241 close(ngctypedb); 242 } 243 } 244 245 void 246 abellopen(void) 247 { 248 /* nothing extra to do with abell: it's directly indexed by number */ 249 if(abelldb == 0) 250 abelldb = eopen("abell"); 251 } 252 253 void 254 patchopen(void) 255 { 256 Biobuf *b; 257 int32 l, m; 258 char buf[100]; 259 260 if(patchdb == 0){ 261 patchdb = eopen("patch"); 262 sprint(buf, "%s/patchindex.scat", dir); 263 b = Bopen(buf, OREAD); 264 if(b == 0){ 265 fprint(2, "can't open %s\n", buf); 266 exits("open"); 267 } 268 for(m=0,l=0; l<=Npatch; l++) 269 patchaddr[l] = m += Bgetc(b)*4; 270 Bterm(b); 271 } 272 } 273 274 void 275 mopen(void) 276 { 277 int i; 278 279 if(mindexdb == 0){ 280 mindexdb = eopen("mindex"); 281 Eread(mindexdb, "mindex", mindex, sizeof mindex); 282 close(mindexdb); 283 for(i=0; i<NMrec; i++) 284 mindex[i].ngc = Short(&mindex[i].ngc); 285 } 286 } 287 288 void 289 constelopen(void) 290 { 291 int i; 292 293 if(condb == 0){ 294 condb = eopen("con"); 295 conindexdb = eopen("conindex"); 296 Eread(conindexdb, "conindex", conindex, sizeof conindex); 297 close(conindexdb); 298 for(i=0; i<Ncon+1; i++) 299 conindex[i] = Short((short*)&conindex[i]); 300 } 301 } 302 303 void 304 lowercase(char *s) 305 { 306 for(; *s; s++) 307 if('A'<=*s && *s<='Z') 308 *s += 'a'-'A'; 309 } 310 311 int 312 loadngc(int32 index) 313 { 314 static int failed; 315 int32 j; 316 317 ngcopen(); 318 j = (index-1)*sizeof(NGCrec); 319 grow(); 320 cur->type = NGC; 321 cur->index = index; 322 seek(ngcdb, j, 0); 323 /* special case: NGC data may not be available */ 324 if(read(ngcdb, &cur->u.ngc, sizeof(NGCrec)) != sizeof(NGCrec)){ 325 if(!failed){ 326 fprint(2, "scat: NGC database not available\n"); 327 failed++; 328 } 329 cur->type = NONGC; 330 cur->u.ngc.ngc = 0; 331 cur->u.ngc.ra = 0; 332 cur->u.ngc.dec = 0; 333 cur->u.ngc.diam = 0; 334 cur->u.ngc.mag = 0; 335 return 0; 336 } 337 cur->u.ngc.ngc = Short(&cur->u.ngc.ngc); 338 cur->u.ngc.ra = Long(&cur->u.ngc.ra); 339 cur->u.ngc.dec = Long(&cur->u.ngc.dec); 340 cur->u.ngc.diam = Long(&cur->u.ngc.diam); 341 cur->u.ngc.mag = Short(&cur->u.ngc.mag); 342 return 1; 343 } 344 345 int 346 loadabell(int32 index) 347 { 348 int32 j; 349 350 abellopen(); 351 j = index-1; 352 grow(); 353 cur->type = Abell; 354 cur->index = index; 355 seek(abelldb, j*sizeof(Abellrec), 0); 356 Eread(abelldb, "abell", &cur->u.abell, sizeof(Abellrec)); 357 cur->u.abell.abell = Short(&cur->u.abell.abell); 358 if(cur->u.abell.abell != index){ 359 fprint(2, "bad format in abell catalog\n"); 360 exits("abell"); 361 } 362 cur->u.abell.ra = Long(&cur->u.abell.ra); 363 cur->u.abell.dec = Long(&cur->u.abell.dec); 364 cur->u.abell.glat = Long(&cur->u.abell.glat); 365 cur->u.abell.glong = Long(&cur->u.abell.glong); 366 cur->u.abell.rad = Long(&cur->u.abell.rad); 367 cur->u.abell.mag10 = Short(&cur->u.abell.mag10); 368 cur->u.abell.pop = Short(&cur->u.abell.pop); 369 cur->u.abell.dist = Short(&cur->u.abell.dist); 370 return 1; 371 } 372 373 int 374 loadsao(int index) 375 { 376 if(index<=0 || index>NSAO) 377 return 0; 378 saoopen(); 379 grow(); 380 cur->type = SAO; 381 cur->index = index; 382 seek(saodb, (index-1)*sizeof(SAOrec), 0); 383 Eread(saodb, "sao", &cur->u.sao, sizeof(SAOrec)); 384 cur->u.sao.ra = Long(&cur->u.sao.ra); 385 cur->u.sao.dec = Long(&cur->u.sao.dec); 386 cur->u.sao.dra = Long(&cur->u.sao.dra); 387 cur->u.sao.ddec = Long(&cur->u.sao.ddec); 388 cur->u.sao.mag = Short(&cur->u.sao.mag); 389 cur->u.sao.mpg = Short(&cur->u.sao.mpg); 390 cur->u.sao.hd = Long(&cur->u.sao.hd); 391 return 1; 392 } 393 394 int 395 loadplanet(int index, Record *r) 396 { 397 if(index<0 || index>NPlanet || planet[index].name[0]=='\0') 398 return 0; 399 grow(); 400 cur->type = Planet; 401 cur->index = index; 402 /* check whether to take new or existing record */ 403 if(r == nil) 404 memmove(&cur->u.planet, &planet[index], sizeof(Planetrec)); 405 else 406 memmove(&cur->u.planet, &r->u.planet, sizeof(Planetrec)); 407 return 1; 408 } 409 410 int 411 loadpatch(int32 index) 412 { 413 int i; 414 415 patchopen(); 416 if(index<=0 || index>Npatch) 417 return 0; 418 grow(); 419 cur->type = Patch; 420 cur->index = index; 421 seek(patchdb, patchaddr[index-1], 0); 422 cur->u.patch.nkey = (patchaddr[index]-patchaddr[index-1])/4; 423 Eread(patchdb, "patch", cur->u.patch.key, cur->u.patch.nkey*4); 424 for(i=0; i<cur->u.patch.nkey; i++) 425 cur->u.patch.key[i] = Long(&cur->u.patch.key[i]); 426 return 1; 427 } 428 429 int 430 loadtype(int t) 431 { 432 int i; 433 434 ngcopen(); 435 for(i=0; i<NNGCrec; i++) 436 if(t == (ngctype[i])){ 437 grow(); 438 cur->type = NGCN; 439 cur->index = i+1; 440 } 441 return 1; 442 } 443 444 void 445 flatten(void) 446 { 447 int i, j, notflat; 448 Record *or; 449 int32 key; 450 451 loop: 452 copy(); 453 reset(); 454 notflat = 0; 455 for(i=0,or=orec; i<norec; i++,or++){ 456 switch(or->type){ 457 default: 458 fprint(2, "bad type %d in flatten\n", or->type); 459 break; 460 461 case NONGC: 462 break; 463 464 case Planet: 465 case Abell: 466 case NGC: 467 case SAO: 468 grow(); 469 memmove(cur, or, sizeof(Record)); 470 break; 471 472 case NGCN: 473 if(loadngc(or->index)) 474 notflat = 1; 475 break; 476 477 case NamedSAO: 478 loadsao(or->index); 479 notflat = 1; 480 break; 481 482 case NamedNGC: 483 if(loadngc(or->index)) 484 notflat = 1; 485 break; 486 487 case NamedAbell: 488 loadabell(or->index); 489 notflat = 1; 490 break; 491 492 case PatchC: 493 loadpatch(or->index); 494 notflat = 1; 495 break; 496 497 case Patch: 498 for(j=1; j<or->u.patch.nkey; j++){ 499 key = or->u.patch.key[j]; 500 if((key&0x3F) == SAO) 501 loadsao((key>>8)&0xFFFFFF); 502 else if((key&0x3F) == Abell) 503 loadabell((key>>8)&0xFFFFFF); 504 else 505 loadngc((key>>16)&0xFFFF); 506 } 507 break; 508 } 509 } 510 if(notflat) 511 goto loop; 512 } 513 514 int 515 ism(int index) 516 { 517 int i; 518 519 for(i=0; i<NMrec; i++) 520 if(mindex[i].ngc == index) 521 return 1; 522 return 0; 523 } 524 525 char* 526 alpha(char *s, char *t) 527 { 528 int n; 529 530 n = strlen(t); 531 if(strncmp(s, t, n)==0 && (s[n]<'a' || 'z'<s[n])) 532 return skipbl(s+n); 533 return 0; 534 535 } 536 537 char* 538 text(char *s, char *t) 539 { 540 int n; 541 542 n = strlen(t); 543 if(strncmp(s, t, n)==0 && (s[n]==0 || s[n]==' ' || s[n]=='\t')) 544 return skipbl(s+n); 545 return 0; 546 547 } 548 549 int 550 cull(char *s, int keep, int dobbox) 551 { 552 int i, j, nobj, keepthis; 553 Record *or; 554 char *t; 555 int dogrtr, doless, dom, dosao, dongc, doabell; 556 int mgrtr, mless; 557 char obj[100]; 558 559 memset(obj, 0, sizeof(obj)); 560 nobj = 0; 561 dogrtr = 0; 562 doless = 0; 563 dom = 0; 564 dongc = 0; 565 dosao = 0; 566 doabell = 0; 567 mgrtr = mless= 0; 568 if(dobbox) 569 goto Cull; 570 for(;;){ 571 if(s[0] == '>'){ 572 dogrtr = 1; 573 mgrtr = 10 * strtod(s+1, &t); 574 if(mgrtr==0 && t==s+1){ 575 fprint(2, "bad magnitude\n"); 576 return 0; 577 } 578 s = skipbl(t); 579 continue; 580 } 581 if(s[0] == '<'){ 582 doless = 1; 583 mless = 10 * strtod(s+1, &t); 584 if(mless==0 && t==s+1){ 585 fprint(2, "bad magnitude\n"); 586 return 0; 587 } 588 s = skipbl(t); 589 continue; 590 } 591 if(t = text(s, "m")){ 592 dom = 1; 593 s = t; 594 continue; 595 } 596 if(t = text(s, "sao")){ 597 dosao = 1; 598 s = t; 599 continue; 600 } 601 if(t = text(s, "ngc")){ 602 dongc = 1; 603 s = t; 604 continue; 605 } 606 if(t = text(s, "abell")){ 607 doabell = 1; 608 s = t; 609 continue; 610 } 611 for(i=0; names[i].name; i++) 612 if(t = alpha(s, names[i].name)){ 613 if(nobj > 100){ 614 fprint(2, "too many object types\n"); 615 return 0; 616 } 617 obj[nobj++] = names[i].type; 618 s = t; 619 goto Continue; 620 } 621 break; 622 Continue:; 623 } 624 if(*s){ 625 fprint(2, "syntax error in object list\n"); 626 return 0; 627 } 628 629 Cull: 630 flatten(); 631 copy(); 632 reset(); 633 if(dom) 634 mopen(); 635 if(dosao) 636 saoopen(); 637 if(dongc || nobj) 638 ngcopen(); 639 if(doabell) 640 abellopen(); 641 for(i=0,or=orec; i<norec; i++,or++){ 642 keepthis = !keep; 643 if(dobbox && inbbox(or->u.ngc.ra, or->u.ngc.dec)) 644 keepthis = keep; 645 if(doless && or->u.ngc.mag <= mless) 646 keepthis = keep; 647 if(dogrtr && or->u.ngc.mag >= mgrtr) 648 keepthis = keep; 649 if(dom && (or->type==NGC && ism(or->u.ngc.ngc))) 650 keepthis = keep; 651 if(dongc && or->type==NGC) 652 keepthis = keep; 653 if(doabell && or->type==Abell) 654 keepthis = keep; 655 if(dosao && or->type==SAO) 656 keepthis = keep; 657 for(j=0; j<nobj; j++) 658 if(or->type==NGC && or->u.ngc.type==obj[j]) 659 keepthis = keep; 660 if(keepthis){ 661 grow(); 662 memmove(cur, or, sizeof(Record)); 663 } 664 } 665 return 1; 666 } 667 668 int 669 compar(const void *va, const void *vb) 670 { 671 Record *a=(Record*)va, *b=(Record*)vb; 672 673 if(a->type == b->type) 674 return a->index - b->index; 675 return a->type - b->type; 676 } 677 678 void 679 sort(void) 680 { 681 int i; 682 Record *r, *s; 683 684 if(nrec == 0) 685 return; 686 qsort(rec, nrec, sizeof(Record), compar); 687 r = rec+1; 688 s = rec; 689 for(i=1; i<nrec; i++,r++){ 690 /* may have multiple instances of a planet in the scene */ 691 if(r->type==s->type && r->index==s->index && r->type!=Planet) 692 continue; 693 memmove(++s, r, sizeof(Record)); 694 } 695 nrec = (s+1)-rec; 696 } 697 698 char greekbuf[128]; 699 700 char* 701 togreek(char *s) 702 { 703 char *t; 704 int i, n; 705 Rune r; 706 707 t = greekbuf; 708 while(*s){ 709 for(i=1; i<=24; i++){ 710 n = strlen(greek[i]); 711 if(strncmp(s, greek[i], n)==0 && (s[n]==' ' || s[n]=='\t')){ 712 s += n; 713 t += runetochar(t, &greeklet[i]); 714 goto Cont; 715 } 716 } 717 n = chartorune(&r, s); 718 for(i=0; i<n; i++) 719 *t++ = *s++; 720 Cont:; 721 } 722 *t = 0; 723 return greekbuf; 724 } 725 726 char* 727 fromgreek(char *s) 728 { 729 char *t; 730 int i, n; 731 Rune r; 732 733 t = greekbuf; 734 while(*s){ 735 n = chartorune(&r, s); 736 for(i=1; i<=24; i++){ 737 if(r == greeklet[i]){ 738 strcpy(t, greek[i]); 739 t += strlen(greek[i]); 740 s += n; 741 goto Cont; 742 } 743 } 744 for(i=0; i<n; i++) 745 *t++ = *s++; 746 Cont:; 747 } 748 *t = 0; 749 return greekbuf; 750 } 751 752 #ifdef OLD 753 /* 754 * Old version 755 */ 756 int 757 coords(int deg) 758 { 759 int i; 760 int x, y; 761 Record *or; 762 int32 dec, ra, ndec, nra; 763 int rdeg; 764 765 flatten(); 766 copy(); 767 reset(); 768 deg *= 2; 769 for(i=0,or=orec; i<norec; i++,or++){ 770 if(or->type == Planet) /* must keep it here */ 771 loadplanet(or->index, or); 772 dec = or->u.ngc.dec/MILLIARCSEC; 773 ra = or->u.ngc.ra/MILLIARCSEC; 774 rdeg = deg/cos((dec*PI)/180); 775 for(y=-deg; y<=+deg; y++){ 776 ndec = dec*2+y; 777 if(ndec/2>=90 || ndec/2<=-90) 778 continue; 779 /* fp errors hurt here, so we round 1' to the pole */ 780 if(ndec >= 0) 781 ndec = ndec*500*60*60 + 60000; 782 else 783 ndec = ndec*500*60*60 - 60000; 784 for(x=-rdeg; x<=+rdeg; x++){ 785 nra = ra*2+x; 786 if(nra/2 < 0) 787 nra += 360*2; 788 if(nra/2 >= 360) 789 nra -= 360*2; 790 /* fp errors hurt here, so we round up 1' */ 791 nra = nra/2*MILLIARCSEC + 60000; 792 loadpatch(patcha(angle(nra), angle(ndec))); 793 } 794 } 795 } 796 sort(); 797 return 1; 798 } 799 #endif 800 801 /* 802 * New version attempts to match the boundaries of the plot better. 803 */ 804 int 805 coords(int deg) 806 { 807 int i; 808 int x, y, xx; 809 Record *or; 810 int32 min, circle; 811 double factor; 812 813 flatten(); 814 circle = 360*MILLIARCSEC; 815 deg *= MILLIARCSEC; 816 817 /* find center */ 818 folded = 0; 819 bbox(0, 0, 0); 820 /* now expand */ 821 factor = cos(angle((decmax+decmin)/2)); 822 if(factor < .2) 823 factor = .2; 824 factor = floor(1/factor); 825 folded = 0; 826 bbox(factor*deg, deg, 1); 827 Bprint(&bout, "%s to ", hms(angle(ramin))); 828 Bprint(&bout, "%s\n", hms(angle(ramax))); 829 Bprint(&bout, "%s to ", dms(angle(decmin))); 830 Bprint(&bout, "%s\n", dms(angle(decmax))); 831 copy(); 832 reset(); 833 for(i=0,or=orec; i<norec; i++,or++) 834 if(or->type == Planet) /* must keep it here */ 835 loadplanet(or->index, or); 836 min = ramin; 837 if(ramin > ramax) 838 min -= circle; 839 for(x=min; x<=ramax; x+=250*60*60){ 840 xx = x; 841 if(xx < 0) 842 xx += circle; 843 for(y=decmin; y<=decmax; y+=250*60*60) 844 if(-circle/4 < y && y < circle/4) 845 loadpatch(patcha(angle(xx), angle(y))); 846 } 847 sort(); 848 cull(nil, 1, 1); 849 return 1; 850 } 851 852 void 853 pplate(char *flags) 854 { 855 int i; 856 int32 c; 857 int na, rah, ram, d1, d2; 858 double r0; 859 int ra, dec; 860 int32 ramin, ramax, decmin, decmax; /* all in degrees */ 861 Record *r; 862 int folded; 863 Angle racenter, deccenter, rasize, decsize, a[4]; 864 Picture *pic; 865 866 rasize = -1.0; 867 decsize = -1.0; 868 na = 0; 869 for(;;){ 870 while(*flags==' ') 871 flags++; 872 if(('0'<=*flags && *flags<='9') || *flags=='+' || *flags=='-'){ 873 if(na >= 3) 874 goto err; 875 a[na++] = getra(flags); 876 while(*flags && *flags!=' ') 877 flags++; 878 continue; 879 } 880 if(*flags){ 881 err: 882 Bprint(&bout, "syntax error in plate\n"); 883 return; 884 } 885 break; 886 } 887 switch(na){ 888 case 0: 889 break; 890 case 1: 891 rasize = a[0]; 892 decsize = rasize; 893 break; 894 case 2: 895 rasize = a[0]; 896 decsize = a[1]; 897 break; 898 case 3: 899 case 4: 900 racenter = a[0]; 901 deccenter = a[1]; 902 rasize = a[2]; 903 if(na == 4) 904 decsize = a[3]; 905 else 906 decsize = rasize; 907 if(rasize<0.0 || decsize<0.0){ 908 Bprint(&bout, "negative sizes\n"); 909 return; 910 } 911 goto done; 912 } 913 folded = 0; 914 /* convert to milliarcsec */ 915 c = 1000*60*60; 916 Again: 917 if(nrec == 0){ 918 Bprint(&bout, "empty\n"); 919 return; 920 } 921 ramin = 0x7FFFFFFF; 922 ramax = -0x7FFFFFFF; 923 decmin = 0x7FFFFFFF; 924 decmax = -0x7FFFFFFF; 925 for(r=rec,i=0; i<nrec; i++,r++){ 926 if(r->type == Patch){ 927 radec(r->index, &rah, &ram, &dec); 928 ra = 15*rah+ram/4; 929 r0 = c/cos(RAD(dec)); 930 ra *= c; 931 dec *= c; 932 if(dec == 0) 933 d1 = c, d2 = c; 934 else if(dec < 0) 935 d1 = c, d2 = 0; 936 else 937 d1 = 0, d2 = c; 938 }else if(r->type==SAO || r->type==NGC || r->type==Abell){ 939 ra = r->u.ngc.ra; 940 dec = r->u.ngc.dec; 941 d1 = 0, d2 = 0, r0 = 0; 942 }else if(r->type==NGCN){ 943 loadngc(r->index); 944 continue; 945 }else if(r->type==NamedSAO){ 946 loadsao(r->index); 947 continue; 948 }else if(r->type==NamedNGC){ 949 loadngc(r->index); 950 continue; 951 }else if(r->type==NamedAbell){ 952 loadabell(r->index); 953 continue; 954 }else 955 continue; 956 if(dec+d2 > decmax) 957 decmax = dec+d2; 958 if(dec-d1 < decmin) 959 decmin = dec-d1; 960 if(folded){ 961 ra -= 180*c; 962 if(ra < 0) 963 ra += 360*c; 964 } 965 if(ra+r0 > ramax) 966 ramax = ra+r0; 967 if(ra < ramin) 968 ramin = ra; 969 } 970 if(!folded && ramax-ramin>270*c){ 971 folded = 1; 972 goto Again; 973 } 974 racenter = angle(ramin+(ramax-ramin)/2); 975 deccenter = angle(decmin+(decmax-decmin)/2); 976 if(rasize<0 || decsize<0){ 977 rasize = angle(ramax-ramin)*cos(deccenter); 978 decsize = angle(decmax-decmin); 979 } 980 done: 981 if(DEG(rasize)>1.1 || DEG(decsize)>1.1){ 982 Bprint(&bout, "plate too big: %s", ms(rasize)); 983 Bprint(&bout, " x %s\n", ms(decsize)); 984 Bprint(&bout, "trimming to 30'x30'\n"); 985 rasize = RAD(0.5); 986 decsize = RAD(0.5); 987 } 988 Bprint(&bout, "%s %s ", hms(racenter), dms(deccenter)); 989 Bprint(&bout, "%s", ms(rasize)); 990 Bprint(&bout, " x %s\n", ms(decsize)); 991 Bflush(&bout); 992 flatten(); 993 pic = image(racenter, deccenter, rasize, decsize); 994 if(pic == 0) 995 return; 996 Bprint(&bout, "plate %s locn %d %d %d %d\n", pic->name, pic->minx, pic->miny, pic->maxx, pic->maxy); 997 Bflush(&bout); 998 displaypic(pic); 999 } 1000 1001 void 1002 lookup(char *s, int doreset) 1003 { 1004 int i, j, k; 1005 int rah, ram, deg; 1006 char *starts, *inputline=s, *t, *u; 1007 Record *r; 1008 Rune c; 1009 int32 n; 1010 double x; 1011 Angle ra; 1012 1013 lowercase(s); 1014 s = skipbl(s); 1015 1016 if(*s == 0) 1017 goto Print; 1018 1019 if(t = alpha(s, "flat")){ 1020 if(*t){ 1021 fprint(2, "flat takes no arguments\n"); 1022 return; 1023 } 1024 if(nrec == 0){ 1025 fprint(2, "no records\n"); 1026 return; 1027 } 1028 flatten(); 1029 goto Print; 1030 } 1031 1032 if(t = alpha(s, "print")){ 1033 if(*t){ 1034 fprint(2, "print takes no arguments\n"); 1035 return; 1036 } 1037 for(i=0,r=rec; i<nrec; i++,r++) 1038 prrec(r); 1039 return; 1040 } 1041 1042 if(t = alpha(s, "add")){ 1043 lookup(t, 0); 1044 return; 1045 } 1046 1047 if(t = alpha(s, "sao")){ 1048 n = strtoul(t, &u, 10); 1049 if(n<=0 || n>NSAO) 1050 goto NotFound; 1051 t = skipbl(u); 1052 if(*t){ 1053 fprint(2, "syntax error in sao\n"); 1054 return; 1055 } 1056 if(doreset) 1057 reset(); 1058 if(!loadsao(n)) 1059 goto NotFound; 1060 goto Print; 1061 } 1062 1063 if(t = alpha(s, "ngc")){ 1064 n = strtoul(t, &u, 10); 1065 if(n<=0 || n>NNGC) 1066 goto NotFound; 1067 t = skipbl(u); 1068 if(*t){ 1069 fprint(2, "syntax error in ngc\n"); 1070 return; 1071 } 1072 if(doreset) 1073 reset(); 1074 if(!loadngc(n)) 1075 goto NotFound; 1076 goto Print; 1077 } 1078 1079 if(t = alpha(s, "ic")){ 1080 n = strtoul(t, &u, 10); 1081 if(n<=0 || n>NIC) 1082 goto NotFound; 1083 t = skipbl(u); 1084 if(*t){ 1085 fprint(2, "syntax error in ic\n"); 1086 return; 1087 } 1088 if(doreset) 1089 reset(); 1090 if(!loadngc(n+NNGC)) 1091 goto NotFound; 1092 goto Print; 1093 } 1094 1095 if(t = alpha(s, "abell")){ 1096 n = strtoul(t, &u, 10); 1097 if(n<=0 || n>NAbell) 1098 goto NotFound; 1099 if(doreset) 1100 reset(); 1101 if(!loadabell(n)) 1102 goto NotFound; 1103 goto Print; 1104 } 1105 1106 if(t = alpha(s, "m")){ 1107 n = strtoul(t, &u, 10); 1108 if(n<=0 || n>NM) 1109 goto NotFound; 1110 mopen(); 1111 for(j=n-1; mindex[j].m<n; j++) 1112 ; 1113 if(doreset) 1114 reset(); 1115 while(mindex[j].m == n){ 1116 if(mindex[j].ngc){ 1117 grow(); 1118 cur->type = NGCN; 1119 cur->index = mindex[j].ngc; 1120 } 1121 j++; 1122 } 1123 goto Print; 1124 } 1125 1126 for(i=1; i<=Ncon; i++) 1127 if(t = alpha(s, constel[i])){ 1128 if(*t){ 1129 fprint(2, "syntax error in constellation\n"); 1130 return; 1131 } 1132 constelopen(); 1133 seek(condb, 4L*conindex[i-1], 0); 1134 j = conindex[i]-conindex[i-1]; 1135 Eread(condb, "con", con, 4*j); 1136 if(doreset) 1137 reset(); 1138 for(k=0; k<j; k++){ 1139 grow(); 1140 cur->type = PatchC; 1141 cur->index = Long(&con[k]); 1142 } 1143 goto Print; 1144 } 1145 1146 if(t = alpha(s, "expand")){ 1147 n = 0; 1148 if(*t){ 1149 if(*t<'0' && '9'<*t){ 1150 Expanderr: 1151 fprint(2, "syntax error in expand\n"); 1152 return; 1153 } 1154 n = strtoul(t, &u, 10); 1155 t = skipbl(u); 1156 if(*t) 1157 goto Expanderr; 1158 } 1159 coords(n); 1160 goto Print; 1161 } 1162 1163 if(t = alpha(s, "plot")){ 1164 if(nrec == 0){ 1165 Bprint(&bout, "empty\n"); 1166 return; 1167 } 1168 plot(t); 1169 return; 1170 } 1171 1172 if(t = alpha(s, "astro")){ 1173 astro(t, 0); 1174 return; 1175 } 1176 1177 if(t = alpha(s, "plate")){ 1178 pplate(t); 1179 return; 1180 } 1181 1182 if(t = alpha(s, "gamma")){ 1183 while(*t==' ') 1184 t++; 1185 u = t; 1186 x = strtod(t, &u); 1187 if(u > t) 1188 gam.gamma = x; 1189 Bprint(&bout, "%.2f\n", gam.gamma); 1190 return; 1191 } 1192 1193 if(t = alpha(s, "keep")){ 1194 if(!cull(t, 1, 0)) 1195 return; 1196 goto Print; 1197 } 1198 1199 if(t = alpha(s, "drop")){ 1200 if(!cull(t, 0, 0)) 1201 return; 1202 goto Print; 1203 } 1204 1205 for(i=0; planet[i].name[0]; i++){ 1206 if(t = alpha(s, planet[i].name)){ 1207 if(doreset) 1208 reset(); 1209 loadplanet(i, nil); 1210 goto Print; 1211 } 1212 } 1213 1214 for(i=0; names[i].name; i++){ 1215 if(t = alpha(s, names[i].name)){ 1216 if(*t){ 1217 fprint(2, "syntax error in type\n"); 1218 return; 1219 } 1220 if(doreset) 1221 reset(); 1222 loadtype(names[i].type); 1223 goto Print; 1224 } 1225 } 1226 1227 switch(s[0]){ 1228 case '"': 1229 starts = ++s; 1230 while(*s != '"') 1231 if(*s++ == 0){ 1232 fprint(2, "bad star name\n"); 1233 return; 1234 } 1235 *s = 0; 1236 if(doreset) 1237 reset(); 1238 j = nrec; 1239 saoopen(); 1240 starts = fromgreek(starts); 1241 for(i=0; i<NName; i++) 1242 if(equal(starts, name[i].name)){ 1243 grow(); 1244 if(name[i].sao){ 1245 rec[j].type = NamedSAO; 1246 rec[j].index = name[i].sao; 1247 } 1248 if(name[i].ngc){ 1249 rec[j].type = NamedNGC; 1250 rec[j].index = name[i].ngc; 1251 } 1252 if(name[i].abell){ 1253 rec[j].type = NamedAbell; 1254 rec[j].index = name[i].abell; 1255 } 1256 strcpy(rec[j].u.named.name, name[i].name); 1257 j++; 1258 } 1259 if(parsename(starts)) 1260 for(i=0; i<NBayer; i++) 1261 if(bayer[i].name[0]==parsed[0] && 1262 (bayer[i].name[1]==parsed[1] || parsed[1]==0) && 1263 bayer[i].name[2]==parsed[2]){ 1264 grow(); 1265 rec[j].type = NamedSAO; 1266 rec[j].index = bayer[i].sao; 1267 strncpy(rec[j].u.named.name, starts, sizeof(rec[j].u.named.name)); 1268 j++; 1269 } 1270 if(j == 0){ 1271 *s = '"'; 1272 goto NotFound; 1273 } 1274 break; 1275 1276 case '0': case '1': case '2': case '3': case '4': 1277 case '5': case '6': case '7': case '8': case '9': 1278 strtoul(s, &t, 10); 1279 if(*t != 'h'){ 1280 BadCoords: 1281 fprint(2, "bad coordinates %s\n", inputline); 1282 break; 1283 } 1284 ra = DEG(getra(s)); 1285 while(*s && *s!=' ' && *s!='\t') 1286 s++; 1287 rah = ra/15; 1288 ra = ra-rah*15; 1289 ram = ra*4; 1290 deg = strtol(s, &t, 10); 1291 if(t == s) 1292 goto BadCoords; 1293 /* degree sign etc. is optional */ 1294 chartorune(&c, t); 1295 if(c == 0xb0) 1296 deg = DEG(getra(s)); 1297 if(doreset) 1298 reset(); 1299 if(abs(deg)>=90 || rah>=24) 1300 goto BadCoords; 1301 if(!loadpatch(patch(rah, ram, deg))) 1302 goto NotFound; 1303 break; 1304 1305 default: 1306 fprint(2, "unknown command %s\n", inputline); 1307 return; 1308 } 1309 1310 Print: 1311 if(nrec == 0) 1312 Bprint(&bout, "empty\n"); 1313 else if(nrec <= 2) 1314 for(i=0; i<nrec; i++) 1315 prrec(rec+i); 1316 else 1317 Bprint(&bout, "%ld items\n", nrec); 1318 return; 1319 1320 NotFound: 1321 fprint(2, "%s not found\n", inputline); 1322 return; 1323 } 1324 1325 char *ngctypes[] = 1326 { 1327 [Galaxy] = "Gx", 1328 [PlanetaryN] = "Pl", 1329 [OpenCl] = "OC", 1330 [GlobularCl] = "Gb", 1331 [DiffuseN] = "Nb", 1332 [NebularCl] = "C+N", 1333 [Asterism] = "Ast", 1334 [Knot] = "Kt", 1335 [Triple] = "***", 1336 [Double] = "D*", 1337 [Single] = "*", 1338 [Uncertain] = "?", 1339 [Nonexistent] = "-", 1340 [Unknown] = " ", 1341 [PlateDefect] = "PD" 1342 }; 1343 1344 char* 1345 ngcstring(int d) 1346 { 1347 if(d<Galaxy || d>PlateDefect) 1348 return "can't happen"; 1349 return ngctypes[d]; 1350 } 1351 1352 short descindex[NINDEX]; 1353 1354 void 1355 printnames(Record *r) 1356 { 1357 int i, ok, done; 1358 1359 done = 0; 1360 for(i=0; i<NName; i++){ /* stupid linear search! */ 1361 ok = 0; 1362 if(r->type==SAO && r->index==name[i].sao) 1363 ok = 1; 1364 if(r->type==NGC && r->u.ngc.ngc==name[i].ngc) 1365 ok = 1; 1366 if(r->type==Abell && r->u.abell.abell==name[i].abell) 1367 ok = 1; 1368 if(ok){ 1369 if(done++ == 0) 1370 Bprint(&bout, "\t"); 1371 Bprint(&bout, " \"%s\"", togreek(name[i].name)); 1372 } 1373 } 1374 if(done) 1375 Bprint(&bout, "\n"); 1376 } 1377 1378 int 1379 equal(char *s1, char *s2) 1380 { 1381 int c; 1382 1383 while(*s1){ 1384 if(*s1==' '){ 1385 while(*s1==' ') 1386 s1++; 1387 continue; 1388 } 1389 while(*s2==' ') 1390 s2++; 1391 c=*s2; 1392 if('A'<=*s2 && *s2<='Z') 1393 c^=' '; 1394 if(*s1!=c) 1395 return 0; 1396 s1++, s2++; 1397 } 1398 return 1; 1399 } 1400 1401 int 1402 parsename(char *s) 1403 { 1404 char *blank; 1405 int i; 1406 1407 blank = strchr(s, ' '); 1408 if(blank==0 || strchr(blank+1, ' ') || strlen(blank+1)!=3) 1409 return 0; 1410 blank++; 1411 parsed[0] = parsed[1] = parsed[2] = 0; 1412 if('0'<=s[0] && s[0]<='9'){ 1413 i = atoi(s); 1414 parsed[0] = i; 1415 if(i > 100) 1416 return 0; 1417 }else{ 1418 for(i=1; i<=24; i++) 1419 if(strncmp(greek[i], s, strlen(greek[i]))==0){ 1420 parsed[0]=100+i; 1421 goto out; 1422 } 1423 return 0; 1424 out: 1425 if('0'<=s[strlen(greek[i])] && s[strlen(greek[i])]<='9') 1426 parsed[1]=s[strlen(greek[i])]-'0'; 1427 } 1428 for(i=1; i<=88; i++) 1429 if(strcmp(constel[i], blank)==0){ 1430 parsed[2] = i; 1431 return 1; 1432 } 1433 return 0; 1434 } 1435 1436 char* 1437 dist_grp(int dg) 1438 { 1439 switch(dg){ 1440 default: 1441 return "unknown"; 1442 case 1: 1443 return "13.3-14.0"; 1444 case 2: 1445 return "14.1-14.8"; 1446 case 3: 1447 return "14.9-15.6"; 1448 case 4: 1449 return "15.7-16.4"; 1450 case 5: 1451 return "16.5-17.2"; 1452 case 6: 1453 return "17.3-18.0"; 1454 case 7: 1455 return ">18.0"; 1456 } 1457 } 1458 1459 char* 1460 rich_grp(int dg) 1461 { 1462 switch(dg){ 1463 default: 1464 return "unknown"; 1465 case 0: 1466 return "30-40"; 1467 case 1: 1468 return "50-79"; 1469 case 2: 1470 return "80-129"; 1471 case 3: 1472 return "130-199"; 1473 case 4: 1474 return "200-299"; 1475 case 5: 1476 return ">=300"; 1477 } 1478 } 1479 1480 char* 1481 nameof(Record *r) 1482 { 1483 NGCrec *n; 1484 SAOrec *s; 1485 Abellrec *a; 1486 static char buf[128]; 1487 int i; 1488 1489 switch(r->type){ 1490 default: 1491 return nil; 1492 case SAO: 1493 s = &r->u.sao; 1494 if(s->name[0] == 0) 1495 return nil; 1496 if(s->name[0] >= 100){ 1497 i = snprint(buf, sizeof buf, "%C", greeklet[s->name[0]-100]); 1498 if(s->name[1]) 1499 i += snprint(buf+i, sizeof buf-i, "%d", s->name[1]); 1500 }else 1501 i = snprint(buf, sizeof buf, " %d", s->name[0]); 1502 snprint(buf+i, sizeof buf-i, " %s", constel[(uchar)s->name[2]]); 1503 break; 1504 case NGC: 1505 n = &r->u.ngc; 1506 if(n->type >= Uncertain) 1507 return nil; 1508 if(n->ngc <= NNGC) 1509 snprint(buf, sizeof buf, "NGC%4d ", n->ngc); 1510 else 1511 snprint(buf, sizeof buf, "IC%4d ", n->ngc-NNGC); 1512 break; 1513 case Abell: 1514 a = &r->u.abell; 1515 snprint(buf, sizeof buf, "Abell%4d", a->abell); 1516 break; 1517 } 1518 return buf; 1519 } 1520 1521 void 1522 prrec(Record *r) 1523 { 1524 NGCrec *n; 1525 SAOrec *s; 1526 Abellrec *a; 1527 Planetrec *p; 1528 int i, rah, ram, dec, nn; 1529 int32 key; 1530 1531 if(r) switch(r->type){ 1532 default: 1533 fprint(2, "can't prrec type %d\n", r->type); 1534 exits("type"); 1535 1536 case Planet: 1537 p = &r->u.planet; 1538 Bprint(&bout, "%s", p->name); 1539 Bprint(&bout, "\t%s %s", 1540 hms(angle(p->ra)), 1541 dms(angle(p->dec))); 1542 Bprint(&bout, " %3.2f° %3.2f°", 1543 p->az/(double)MILLIARCSEC, p->alt/(double)MILLIARCSEC); 1544 Bprint(&bout, " %s", 1545 ms(angle(p->semidiam))); 1546 if(r->index <= 1) 1547 Bprint(&bout, " %g", p->phase); 1548 Bprint(&bout, "\n"); 1549 break; 1550 1551 case NGC: 1552 n = &r->u.ngc; 1553 if(n->ngc <= NNGC) 1554 Bprint(&bout, "NGC%4d ", n->ngc); 1555 else 1556 Bprint(&bout, "IC%4d ", n->ngc-NNGC); 1557 Bprint(&bout, "%s ", ngcstring(n->type)); 1558 if(n->mag == UNKNOWNMAG) 1559 Bprint(&bout, "----"); 1560 else 1561 Bprint(&bout, "%.1f%c", n->mag/10.0, n->magtype); 1562 Bprint(&bout, "\t%s %s\t%c%.1f'\n", 1563 hm(angle(n->ra)), 1564 dm(angle(n->dec)), 1565 n->diamlim, 1566 DEG(angle(n->diam))*60.); 1567 prdesc(n->desc, desctab, descindex); 1568 printnames(r); 1569 break; 1570 1571 case Abell: 1572 a = &r->u.abell; 1573 Bprint(&bout, "Abell%4d %.1f %.2f° %dMpc", a->abell, a->mag10/10.0, 1574 DEG(angle(a->rad)), a->dist); 1575 Bprint(&bout, "\t%s %s\t%.2f %.2f\n", 1576 hm(angle(a->ra)), 1577 dm(angle(a->dec)), 1578 DEG(angle(a->glat)), 1579 DEG(angle(a->glong))); 1580 Bprint(&bout, "\tdist grp: %s rich grp: %s %d galaxies/°²\n", 1581 dist_grp(a->distgrp), 1582 rich_grp(a->richgrp), 1583 a->pop); 1584 printnames(r); 1585 break; 1586 1587 case SAO: 1588 s = &r->u.sao; 1589 Bprint(&bout, "SAO%6ld ", r->index); 1590 if(s->mag==UNKNOWNMAG) 1591 Bprint(&bout, "---"); 1592 else 1593 Bprint(&bout, "%.1f", s->mag/10.0); 1594 if(s->mpg==UNKNOWNMAG) 1595 Bprint(&bout, ",---"); 1596 else 1597 Bprint(&bout, ",%.1f", s->mpg/10.0); 1598 Bprint(&bout, " %s %s %.4fs %.3f\"", 1599 hms(angle(s->ra)), 1600 dms(angle(s->dec)), 1601 DEG(angle(s->dra))*(4*60), 1602 DEG(angle(s->ddec))*(60*60)); 1603 Bprint(&bout, " %.3s %c %.2s %ld %d", 1604 s->spec, s->code, s->compid, s->hd, s->hdcode); 1605 if(s->name[0]) 1606 Bprint(&bout, " \"%s\"", nameof(r)); 1607 Bprint(&bout, "\n"); 1608 printnames(r); 1609 break; 1610 1611 case Patch: 1612 radec(r->index, &rah, &ram, &dec); 1613 Bprint(&bout, "%dh%dm %d°", rah, ram, dec); 1614 key = r->u.patch.key[0]; 1615 Bprint(&bout, " %s", constel[key&0xFF]); 1616 if((key>>=8) & 0xFF) 1617 Bprint(&bout, " %s", constel[key&0xFF]); 1618 if((key>>=8) & 0xFF) 1619 Bprint(&bout, " %s", constel[key&0xFF]); 1620 if((key>>=8) & 0xFF) 1621 Bprint(&bout, " %s", constel[key&0xFF]); 1622 for(i=1; i<r->u.patch.nkey; i++){ 1623 key = r->u.patch.key[i]; 1624 switch(key&0x3F){ 1625 case SAO: 1626 Bprint(&bout, " SAO%ld", (key>>8)&0xFFFFFF); 1627 break; 1628 case Abell: 1629 Bprint(&bout, " Abell%ld", (key>>8)&0xFFFFFF); 1630 break; 1631 default: /* NGC */ 1632 nn = (key>>16)&0xFFFF; 1633 if(nn > NNGC) 1634 Bprint(&bout, " IC%d", nn-NNGC); 1635 else 1636 Bprint(&bout, " NGC%d", nn); 1637 Bprint(&bout, "(%s)", ngcstring(key&0x3F)); 1638 break; 1639 } 1640 } 1641 Bprint(&bout, "\n"); 1642 break; 1643 1644 case NGCN: 1645 if(r->index <= NNGC) 1646 Bprint(&bout, "NGC%ld\n", r->index); 1647 else 1648 Bprint(&bout, "IC%ld\n", r->index-NNGC); 1649 break; 1650 1651 case NamedSAO: 1652 Bprint(&bout, "SAO%ld \"%s\"\n", r->index, togreek(r->u.named.name)); 1653 break; 1654 1655 case NamedNGC: 1656 if(r->index <= NNGC) 1657 Bprint(&bout, "NGC%ld \"%s\"\n", r->index, togreek(r->u.named.name)); 1658 else 1659 Bprint(&bout, "IC%ld \"%s\"\n", r->index-NNGC, togreek(r->u.named.name)); 1660 break; 1661 1662 case NamedAbell: 1663 Bprint(&bout, "Abell%ld \"%s\"\n", r->index, togreek(r->u.named.name)); 1664 break; 1665 1666 case PatchC: 1667 radec(r->index, &rah, &ram, &dec); 1668 Bprint(&bout, "%dh%dm %d\n", rah, ram, dec); 1669 break; 1670 } 1671 }