Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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