1 /*
2  * xvpds.c - load routine for astronomical PDS/VICAR format pictures
3  * version of 6-4-93
4  *
5  * Anthony A. Datri
6  * Siemens Corporate Research
7  * 755 College Road East
8  * Princeton NJ 08540
9  * aad@scr.siemens.com
10  *
11  * Alexandra Aarhus-Dusenberg
12  * alexd@world.std.com
13  *
14  * 5-3-93    added leading spaces back into sscanf's -- some huffmanized
15  	       files have them.
16  * 5-2-93    slapped in Peter G. Ford <pgf@space.mit.edu>'s patches for
17  	     xv 3.00:
18    Adds code to Makefile to compile under SunOS 4.0.3 (as if anybody cares).
19    Improves PDS/VICAR file recognition.
20    Writes current directory name in Doggie's window title.
21    Handles (most) PDS and VICAR images correctly.
22    Choice of algorithm for 16->8 bit conversion--linear or histogram stretch.
23     (adds CONV24_HIST item in "24/8 bit" pull-down menu.)
24    Uses any "palette.tab" file in cwd to color PDS/VICAR image.
25 
26  * 9-2-91    began integration.	 Much of this code is lifted from vicar.c,
27  	     which I wrote for xloadimage.  This is a little simpler, though.
28 
29  * 10-17-91  pdsuncomp is called with system(), which typically feeds the
30  	     commandline to sh.  Make sure that your .profile adds wherever
31  	     you have pdsuncomp to the PATH, like
32 
33  		PATH=$PATH:/usr/local/bin
34 
35  * 11-15-91  substituted vdcomp from Viking CD's for pdsuncomp. I added
36              recognition of - and shut off various messages
37 
38  * 1-5-92    merged into xv rel 2
39 
40  * 3-11-92   cleaned up some comments
41 
42  * 3-24-92   Got some new CD's from NASA of mosics and other processed Viking
43              stuff.  There are actually records terminated with CRNLCR in
44              these images, as well as ones that identify the spacecraft name
45              as {VIKING_ORBITER_1, VIKING_ORBITER_2}.  I hacked up the code
46              yet further to deal with these.  There's a Sun 4 XView binary for
47              an image display program on these discs, but it's nowhere near as
48              neat as the good Mr. Bradley's XV.
49 
50 
51  * Sources of these CD's:
52  *
53  *  National Space Science Data Center
54  *  Goddard Space Flight Center
55  *  Code 933.4
56  *  Greenbelt, Maryland
57  *  (301) 286-6695
58  *   or call
59  *  (301) 286-9000 (300,1200,2400 bps)
60  *   or telnet
61  *  nssdca.gsfc.nasa.gov (128.183.10.4) and log in as 'NODIS' (no password).
62  *
63  *  Randy Davis
64  *  LASP
65  *  University of Colorado
66  *  Boulder CO 80309-0392
67  *  (303) 492-6867
68  *
69  * These CD's are reasonably priced.  Encourage the continue production
70  * of them by buying a set.
71  *
72  * There are three types of files that we deal with here.  I'll call them
73  * PDS-labeled, PDS-labeled Huffman-encoded, and VICAR.  Each consists of data
74  * prefixed with a set of ASCII headers.  PDS-labeled and VICAR files are raw
75  * grayscale data, the dimensions of which can be found from the headers.
76  * PDS-labeled, Huffman-encoded files have the image information adaptively
77  * Huffman-encoded, and the encoding histogram follows the ASCII headers.
78  * To decode these, we use a slightly modified version of "vdcomp.c" from the
79  * NASA Viking CD-ROMS.  For xv to work, you need to have vdcomp compiled
80  * and in your search path.  vdcomp.c should be included with this distribution.
81  *
82  * I've heard that newer discs have FITS images on them.  If they do, support
83  * for them will be added when I get one.  Until then, you can use fitstopgm.
84  *
85  * LoadPDS(fname, numcols)  -  coerces a PDS/VICAR image
86  * WriteVICAR(fp, pic, w, h, r,g,b, numcols, style)
87  */
88 
89 /*
90  * Copyright 1989, 1990 by Anthony A. Datri
91  *
92  * Permission to use, copy, and distribute for non-commercial purposes,
93  * is hereby granted without fee, providing that the above copyright
94  * notice appear in all copies, that both the copyright notice and this
95  * permission notice appear in supporting documentation.
96  *
97  * In exception to the above, permission to John Bradley is hereby granted to
98  * distribute this code as he sees fit within the context of his "xv" image
99  * viewer.
100  *
101  * This software is provided "as is" without any express or implied warranty.
102  */
103 
104 #define  NEEDSDIR       /* for S_IRUSR|S_IWUSR */
105 #include "xv.h"
106 
107 #ifdef HAVE_PDS
108 
109 
110 #define PDSFIXED        (1)
111 #define PDSVARIABLE     (2)
112 #define PDSTRASH        (-1)
113 #define VICAR           (3)
114 #define VIKINGFIXED     (4) /* Viking discs have a unique variant */
115 #define VIKINGVARIABLE  (5)
116 
117 
118 #ifndef TRUE
119 #define TRUE (1)
120 #define FALSE (0)
121 #endif
122 
123 #define MAX_SIZE	20480   /* a guess -- even magellan images aren't
124 				    bigger than 2k x 2k */
125 #define RTBUFFSIZE	62	/* small buffer for magic */
126 #define FNAMESIZE	1024	/* too hard to generalize really getting it */
127 #define PDSUNCOMP	"vdcomp" /* filter to un-Huffmanize */
128 
129 /* This is arbitrary.  Everything I've seen so far fits in 50 chars */
130 #define COMMENTSIZE	50
131 #define INOTESIZE	1000
132 
133 
134 static int	lastwasinote = FALSE;
135 static char	scanbuff         [MAX_SIZE+1],
136                 rtbuff         [RTBUFFSIZE+1],
137 		inote	        [INOTESIZE+1],
138                 infobuff      [COMMENTSIZE+1],
139 		spacecraft    [COMMENTSIZE+1],
140 		target        [COMMENTSIZE+1],
141 		filtname      [COMMENTSIZE+1],
142 		gainmode      [COMMENTSIZE+1],
143 		editmode      [COMMENTSIZE+1],
144 		scanmode      [COMMENTSIZE+1],
145 		exposure      [COMMENTSIZE+1],
146 		shuttermode   [COMMENTSIZE+1],
147 		mphase        [COMMENTSIZE+1],
148 		iname         [COMMENTSIZE+1],
149 		itime         [COMMENTSIZE+1],
150                 garbage       [1024],
151 		*tmptmp,
152 		pdsuncompfname[FNAMESIZE];
153 
154 #define SSTR(l)			"%" #l "s"
155 #define S(l)			SSTR(l)
156 
157 byte *image;
158 static int elaphe;
159 
160 
161 
162 /* local function declarations */
163 static int getpdsrec          PARM((FILE *, char *));
164 static int Convert16BitImage  PARM((char *, PICINFO *, int));
165 static int LoadPDSPalette     PARM((char *, PICINFO *));
166 
167 
168 
169 /* Get a NULL-, CR-, or CRLF-terminated record from a PDS file  */
170 /* The method of termination is unpredictable.  A pox on VMS.   */
171 /* Sometimes we even get mixtures of CR's and LF's.  Sigh.      */
172 
getpdsrec(f,buff)173 static int getpdsrec(f,buff)
174      FILE *f;
175      char *buff;
176 {
177   static char *bp;
178   static int count;
179   int c;
180 
181   /* read any leading CR's or LF's (sigh) */
182   *buff='\0';
183   bp=buff;
184   count = 0;
185   while (count==0) {
186     c=fgetc(f);
187     switch (c) {
188     case EOF: return(0);
189     case '\r':
190     case '\n': continue;
191     default:  ungetc(c,f);
192       count = -1;
193     }
194   }
195 
196   count=0;
197   bp=buff;
198   while (1) {
199     c=fgetc(f);
200     switch (c) {
201 
202     case '\r':  *bp='\0';
203                 switch (c=fgetc(f)) {
204 		  case EOF:
205 		  case '\n':  break;
206                   default:    ungetc(c,f);
207 		  }
208                 return(count);
209 
210     case EOF:	*bp='\0';  return(count);
211 
212     case '\0':  return(count);
213 
214     default:	count++;  *bp++ = c;
215     }
216   }
217 }
218 
219 
220 /* A VICAR image seems to begin with a one-line header describing all sorts
221  * of things, almost all of which don't mean much to Joe User, who just
222  * wants to see what Venus looks like.  The first thing in this label is a
223  * string defining the size of the label, like "LBLSIZE=2048".  Then there's
224  * other crud of variable usefulness.  Important to us are the NL and NS
225  * fields, which seem to tell us the number of columns and rows, respectively.
226  * Among these other fields seem to be the local map coordinates of the region.
227  * These files are easy -- you can even convert one to usefulness by grabbing
228  * the dimensions and lblsize and feeding to rawtopgm.  Example, if lblsize,
229  * nl, and ns are 2048, 1536, and 2048, respectively, use "rawtopgm -headerskip
230  * 2048 2048 1536 <foo.img >foo.pgm.  All of the samples I've seen are 8 bits
231  * deep, so we assume that.  Yeah, yeah, that's nasty and evil, and I'm
232  * terribly ashamed of it, but I'm not going to bother with bit fiddling
233  * until I see an image that needs it.  I once had a 16-bit Galileo image of
234  * Venus, but it got lost (sigh).
235  *
236  * These images are typically fairly big, so it makes sense to verify the
237  * format before we try to read it all in.
238  *
239  * PDS images are prefixed like this:
240  *
241  *  od -a c2061139.imq | head -a
242  *  0000000    - nul   N   J   P   L   1   I   0   0   P   D   S   1   0   0
243  *
244  * so we'll read off the first two chars, which seem to be a length field
245  * of some kind, then look for NJPL..  Images on the Sampler
246  * disc seem to leave off the first two bytes.  Sigh.  This may sometimes be
247  * a distinction between the fixed and variable-record files.
248  */
249 
250 /*******************************************/
LoadPDS(fname,pinfo)251 int LoadPDS(fname, pinfo)
252      char    *fname;
253      PICINFO *pinfo;
254 {
255   /* returns '1' on success, '0' on failure */
256 
257   int tempnum, bytewidth, bufsize;
258 #ifndef USE_MKSTEMP
259   int tmpfd;
260 #endif
261   FILE	*zf;
262   static int isfixed,teco,i,j,itype,vaxbyte,
263              recsize,hrecsize,irecsize,isimage,labelrecs,labelsofar,
264              w,h,lpsize,lssize,samplesize,returnp,labelsize,yy;
265   char	*tmp;
266   const char   *ftypstr;
267   unsigned long filesize;
268   char  sampletype[64];
269 
270   pinfo->type = PIC8;
271   isfixed = TRUE;
272   returnp = isimage = FALSE;
273   itype   = PDSTRASH;
274 
275   teco = i = j = recsize = hrecsize = irecsize = labelrecs = w = h = 0;
276   lpsize = lssize = samplesize = labelsize = labelsofar = 0;
277 
278   (*pdsuncompfname) = (*iname) = (*target) = (*filtname) = (*garbage) = '\0';
279   (*itime) = (*spacecraft) = (*scanbuff) = (*mphase) = (*rtbuff) = '\0';
280   (*sampletype) = (*inote) = (*infobuff) = (*gainmode) = (*editmode) = '\0';
281   (*scanmode) = (*exposure) = (*shuttermode) = '\0';
282 
283   /* there may be some wisdom in statically mallocing space for an 800x800
284      image, since that's what the Voyager cd's have, or even a 2kx2k, since
285      some of the Magellan images from Ames are that size */
286 
287   zf = xv_fopen(fname,"r");
288   if (!zf) {
289     SetISTR(ISTR_WARNING,"LoadPDS: can't open %s\n",fname);
290     return 0;
291   }
292 
293   /* figure out the file size (for Informational Purposes Only) */
294   fseek(zf, 0L, 2);
295   filesize = ftell(zf);
296   fseek(zf, 0L, 0);
297 
298   /* read the magic.  If it's got two bytes of crud in front of it, it's
299      a screwy "variable-length-record" file. We have to jump through
300      bloody VAX hoops to find out how long the record is.  Sigh.  Have
301      people never heard of newlines? */
302 
303   if ((teco=fread(rtbuff,(size_t) 13, (size_t) 1,zf)) != 1) {
304     SetISTR(ISTR_WARNING,"LoadPDS: couldn't read magic #: %d,%s",teco,fname);
305     fclose(zf);
306     return 0;
307   }
308 
309   /* this makes sscanf so much happier */
310   rtbuff[RTBUFFSIZE-1] = '\0';
311 
312   if (strncmp(rtbuff, "NJPL1I00PDS", (size_t) 11) == 0) {
313     itype=PDSFIXED;
314   } else if (strncmp(rtbuff+2,"NJPL1I00PDS", (size_t) 11) == 0) {
315     itype=PDSVARIABLE;
316   } else if (strncmp(rtbuff,"CCSD3Z", (size_t) 6) == 0) {
317     itype=VIKINGFIXED; /* unique variant of PDS on Viking discs (browse) */
318   } else if (strncmp(rtbuff+2,"CCSD3Z", (size_t) 6) == 0) {
319     itype=VIKINGVARIABLE;
320   } else if (sscanf(rtbuff,"LBLSIZE = %d%n",&labelsize,&labelsofar) == 1) {
321     itype=VICAR;
322   } else {
323     SetISTR(ISTR_WARNING,"LoadPDS: can't handle this file, for some reason\n");
324     fclose(zf);
325     return 0;
326   }
327 
328   switch (itype) {
329   case PDSFIXED:
330   case VICAR:
331   case VIKINGFIXED:     isfixed=TRUE;
332                         sprintf(pinfo->fullInfo,
333 				"PDS/VICAR, 8 bits per pixel. (%ld bytes)",
334 				filesize);
335                         break;
336   case PDSVARIABLE:
337   case VIKINGVARIABLE:  isfixed=FALSE;
338                         sprintf(pinfo->fullInfo,
339 			"Huffman-encoded PDS, 8 bits per pixel. (%ld bytes)",
340 				filesize);
341   }
342 
343   if ((itype == PDSFIXED) || (itype == PDSVARIABLE) ||
344       (itype == VIKINGFIXED) || (itype == VIKINGVARIABLE)) {
345 
346     if (isfixed == FALSE) {
347       teco = ((*rtbuff) + ((*(rtbuff+1)) << 8));
348       fread(rtbuff, (size_t) ((teco%2 ? teco+1 : teco) - 11),(size_t) 1, zf);
349     }
350 
351     /* We know that we've got a PDS file of some sort.  We have to
352        search through the labels looking for things because usage doesn't seem
353        to be consistent, or even predictable. */
354 
355     for (;;) {
356       if (isfixed) {
357 	if (getpdsrec(zf,scanbuff) == 0) {
358 	  SetISTR(ISTR_WARNING,"corrupt or incomplete PDS labels (low fread)");
359 	  break;
360 	}
361       } else {
362 	/* AAAARGGGGH!  We've got a bloody variable-length file where
363 	 * the headers are preceded by a byte count, as a VAX 16-bit
364 	 * integer of all things.
365   	 *
366 	 * "A compressed image file is composed of variable-length records
367 	 * defined according to the ISO standard.  Each variable length
368 	 * record starts with a record length indicator, stored as a 16-bit
369 	 * integer, followed by the number of bytes indicated in the record
370 	 * length indicator. If the length indicator is odd, then a pad byte
371 	 * is appended to the end of the record so that all records contain
372 	 * an even number of bytes." */
373 
374 	i=getc(zf);
375 	j=getc(zf);
376 	if (j == EOF) {
377 	  SetISTR(ISTR_WARNING,"LoadPDS: j is EOF\n");
378 	  fclose(zf);
379 	  return 0;
380 	}
381 
382 	teco = i + (j << 8);
383 	if (teco % 2) teco++;
384 
385 	if (fread(scanbuff, (size_t) teco, (size_t) 1, zf) != 1) {
386 	  SetISTR(ISTR_WARNING,"LoadPDS: bad fread reading labels\n");
387 	  fclose(zf);
388 	  return 0;
389 	}
390 
391 	scanbuff[teco]='\0';
392       }
393 
394       /* otay, we've managed to wrestle a header of some sort from the
395 	 file -- now we grep through until we hit the END, at which point
396 	 we break out of the loop and see what we've got.  In the future,
397 	 we'll check for informational headers and write them out if the
398 	 fool user wants them.  There seems to be disagreement about what
399 	 the headers are called, since we might find, for example, either
400 	 TARGET_BODY or TARGET_NAME.  Bloody 'ell.  I think the problem
401 	 is that there isn't an actual *standard* for these formats, or
402 	 the standard isn't widely available, so
403 	 people kinda make it up as they go along. */
404 
405       if (strcmp(scanbuff,"END") == 0) {
406 	break;
407       } else if (sscanf(scanbuff, " RECORD_TYPE = " S(RTBUFFSIZE), rtbuff) == 1) {
408 	if (strncmp(rtbuff,"VARIABLE_LENGTH", (size_t) 15) == 0) {
409 	  /*		itype=PDSVARIABLE; */
410 	} else if (strncmp(rtbuff,"FIXED_LENGTH", (size_t) 12) == 0) {
411 	  /*		itype=PDSFIXED;*/
412 	} else {
413 	  rtbuff[RTBUFFSIZE-1]='\0'; /* juuuust in case */
414 	  SetISTR(ISTR_WARNING,
415 		  "LoadPDS: unsupported record type \"%s\"\n",rtbuff);
416 	  fclose(zf);
417 	  return 0;
418 	}
419 	lastwasinote=FALSE;
420       } else if (sscanf(scanbuff," RECORD_BYTES = %d",&recsize) == 1) {
421 	/* these default to RECORD_BYTES unless explicitly set */
422 	if (hrecsize == 0) hrecsize=recsize;
423 	    if (irecsize == 0) irecsize=recsize;
424 	lastwasinote=FALSE;
425 	continue;
426       } else if (sscanf(scanbuff, " FILE_TYPE = " S(RTBUFFSIZE), rtbuff) != 0) {
427 	lastwasinote=FALSE;
428 	if (strncmp(rtbuff,"IMAGE", (size_t) 5) == 0) {
429 	  isimage=TRUE;
430 	  continue;
431 	} else {
432 	  isimage=FALSE;
433 	  break;
434 	}
435       } else if ((sscanf(scanbuff," HEADER_RECORDS = %d",&labelrecs) == 1) ||
436 		 (sscanf(scanbuff," LABEL_RECORDS = %d", &labelrecs) == 1)) {
437 	lastwasinote=FALSE;
438 	continue;
439       } else if (sscanf(scanbuff," IMAGE_LINES = %d",&h) == 1) {
440 	isimage=TRUE; lastwasinote=FALSE; continue;
441       } else if (sscanf(scanbuff," LINE_SAMPLES = %d",&w) == 1) {
442 	lastwasinote=FALSE; continue;
443       } else if (sscanf(scanbuff," LINES = %d",&h) == 1) {
444 	isimage=TRUE; lastwasinote=FALSE; continue;
445       } else if (sscanf(scanbuff," HEADER_RECORD_BYTES = %d",&hrecsize)==1) {
446 	lastwasinote=FALSE; continue;
447       } else if (sscanf(scanbuff," IMAGE_RECORD_BYTES = %d",&irecsize)==1) {
448 	lastwasinote=FALSE; continue;
449       } else if (sscanf(scanbuff," LINE_PREFIX_BYTES = %d",&lpsize)==1) {
450 	lastwasinote=FALSE; continue;
451       } else if (sscanf(scanbuff," LINE_SUFFIX_BYTES = %d",&lssize)==1) {
452 	lastwasinote=FALSE; continue;
453       } else if (sscanf(scanbuff," SAMPLE_BITS = %d", &samplesize) == 1) {
454 	lastwasinote=FALSE; continue;
455       } else if (sscanf(scanbuff, " SAMPLE_TYPE = " S(64), sampletype) == 1) {
456 	lastwasinote=FALSE; continue;
457       } else if (sscanf(scanbuff," SPACECRAFT_NAME = " S(COMMENTSIZE) " " S(1023),
458 			spacecraft,garbage) == 2 ) {
459 	const char *tmp = xv_strstr(scanbuff, spacecraft) + strlen(spacecraft);
460 	strncat(spacecraft, tmp, COMMENTSIZE - strlen(spacecraft));
461 	lastwasinote=FALSE;  continue;
462       } else if (sscanf(scanbuff, " SPACECRAFT_NAME = " S(COMMENTSIZE), spacecraft) == 1) {
463 	lastwasinote=FALSE; continue;
464 
465       } else if (sscanf(scanbuff, " TARGET_NAME = " S(COMMENTSIZE), target) == 1) {
466 	lastwasinote=FALSE; continue;
467       } else if (sscanf(scanbuff, " TARGET_BODY = " S(COMMENTSIZE), target) == 1) {
468 	lastwasinote=FALSE; continue;
469 
470       } else if (sscanf(scanbuff, " MISSION_PHASE_NAME = " S(COMMENTSIZE), mphase) == 1) {
471 	lastwasinote=FALSE; continue;
472       } else if (sscanf(scanbuff, " MISSION_PHASE = " S(COMMENTSIZE), mphase) == 1) {
473 	lastwasinote=FALSE; continue;
474 
475       } else if (sscanf(scanbuff, " INSTRUMENT_NAME = " S(COMMENTSIZE), iname) == 1) {
476 	lastwasinote=FALSE; continue;
477 
478       } else if (sscanf(scanbuff, " GAIN_MODE_ID = " S(COMMENTSIZE), gainmode) == 1) {
479 	lastwasinote=FALSE; continue;
480 
481       } else if (sscanf(scanbuff, " INSTRUMENT_GAIN_STATE = " S(COMMENTSIZE), gainmode) ==1 ) {
482 	lastwasinote=FALSE; continue;
483 
484       } else if (sscanf(scanbuff, " EDIT_MODE_ID = " S(COMMENTSIZE), editmode) == 1) {
485 	lastwasinote=FALSE; continue;
486 
487       } else if (sscanf(scanbuff, " INSTRUMENT_EDIT_MODE = " S(COMMENTSIZE), editmode) == 1) {
488 	lastwasinote=FALSE; continue;
489 
490       } else if (sscanf(scanbuff, " SCAN_MODE_ID = " S(COMMENTSIZE), scanmode) == 1) {
491 	lastwasinote=FALSE; continue;
492 
493       } else if (sscanf(scanbuff, " INSTRUMENT_SCAN_RATE = " S(COMMENTSIZE), scanmode) == 1) {
494 	lastwasinote=FALSE; continue;
495 
496       } else if (sscanf(scanbuff, " SHUTTER_MODE_ID = " S(COMMENTSIZE), shuttermode) == 1) {
497 	lastwasinote=FALSE; continue;
498 
499       } else if (sscanf(scanbuff, " INSTRUMENT_SHUTTER_MODE = " S(COMMENTSIZE), shuttermode) == 1) {
500 	lastwasinote=FALSE; continue;
501 
502       } else if (sscanf(scanbuff, " SCAN_MODE_ID = " S(COMMENTSIZE), scanmode) == 1) {
503 	lastwasinote=FALSE; continue;
504 
505       } else if (sscanf(scanbuff, " INSTRUMENT_SCAN_RATE = " S(COMMENTSIZE), scanmode) == 1) {
506 	lastwasinote=FALSE; continue;
507 
508       } else if (sscanf(scanbuff, " SPACECRAFT_EVENT_TIME = " S(COMMENTSIZE), itime) == 1) {
509 	lastwasinote=FALSE; continue;
510 
511       } else if (sscanf(scanbuff, " IMAGE_TIME = " S(COMMENTSIZE), itime) == 1) {
512 	lastwasinote=FALSE; continue;
513 
514       } else if (sscanf(scanbuff, " FILTER_NAME = " S(COMMENTSIZE), filtname) == 1) {
515 	lastwasinote=FALSE; continue;
516 
517       } else if (sscanf(scanbuff, " INSTRUMENT_FILTER_NAME = " S(COMMENTSIZE), filtname) == 1) {
518 	lastwasinote=FALSE; continue;
519 
520       } else if ((sscanf(scanbuff, " EXPOSURE_DURATION = " S(COMMENTSIZE), exposure) == 1)
521 	      || (sscanf(scanbuff, " INSTRUMENT_EXPOSURE_DURATION = " S(COMMENTSIZE),
522 			 exposure)) == 1) {
523 	tmptmp = (char *) index(scanbuff,'=');
524 	tmptmp++;
525 	while((*tmptmp) == ' ')
526 	    tmptmp++;
527 	strcpy(exposure,tmptmp);
528 	lastwasinote=FALSE; continue;
529 
530       } else if (sscanf(scanbuff, "NOTE = " S(INOTESIZE), inote) == 1) {
531 	tmptmp = (char *) index(scanbuff,'='); tmptmp++;
532 	while (((*tmptmp) == ' ') || ((*tmptmp)  == '"')) tmptmp++;
533 	strncpy(inote, tmptmp, INOTESIZE - 1);
534 	strcat(inote," ");
535 
536 	/*   evil and somewhat risky:  A "note" (really, any textual
537 	 *    field) can span records until " is reached.  If I ever
538 	 *     get my hands on the clown who designed this format...
539 	 *                             What we basically assume here
540 	 *        is that a NOTE record that doesn't end with a " is
541 	 *    followed by some number of continuations, one of which
542 	 *   will have a " in it.  If this turns out to not be true,
543 	 *          well, we'll segmentation fault real soon. We use
544 	 * lastwasinote as a semaphore to indicate that the previous
545 	 *       record was an unfinished NOTE record.  We clear the
546 	 *      flag in each of the above record types for potential
547 	 *   error recovery, although it really breaks up the beauty
548 	 * of the cascading sscanfs.  Dykstra'd love me for this one */
549 
550 	if (inote[strlen(inote)-1] != '"')
551 	    lastwasinote=TRUE;
552 	else lastwasinote=FALSE;
553 	continue;
554 
555       } else if (lastwasinote) {
556 	tmptmp=scanbuff;
557 	while (((*tmptmp) == ' ') || ((*tmptmp)  == '"')) tmptmp++;
558 	strncat(inote, tmptmp, INOTESIZE - strlen(inote) - 1);
559 	strcat(inote," ");
560 	if (index(tmptmp,'"') != NULL)
561 	    lastwasinote=FALSE;
562 	continue;
563       }
564     }
565 
566     elaphe=strlen(inote)-1;
567     while ( (inote[elaphe] == ' ') || (inote[elaphe] == '"')) {
568 	inote[elaphe--]='\0';
569     }
570     if ((isimage == TRUE) && (labelsize=(labelrecs * hrecsize))) {
571       ;
572     } else {
573       SetISTR(ISTR_WARNING,
574 	      "This looks like a PDS/VICAR image, but not enough.\n");
575       fclose(zf);
576       return 0;
577     }
578 
579     vaxbyte = strncmp(sampletype, "VAX_", (size_t) 4) == 0 ||
580       strncmp(sampletype, "LSB_", (size_t) 4) == 0;
581 
582   } else if (itype == VICAR) {
583     /* we've got a VICAR file.  Let's find out how big the puppy is */
584     ungetc(' ', zf);
585     --labelsofar;
586 
587     if (fread(scanbuff, (size_t) (labelsize-labelsofar),(size_t) 1,zf) == 1) {
588       if ((tmp = (char *) xv_strstr(scanbuff," NL=")) == NULL) {
589 	SetISTR(ISTR_WARNING,"LoadPDS: bad NL in VICAR\n");
590 	returnp=TRUE;
591       }
592 
593       if (sscanf(tmp," NL = %d",&h) != 1) {
594 	SetISTR(ISTR_WARNING,"LoadPDS: bad scan NL in VICAR\n");
595 	returnp=TRUE;
596       }
597 
598       if ((tmp = (char *) xv_strstr(scanbuff, " NS=")) == NULL) {
599 	SetISTR(ISTR_WARNING,"LoadPDS: bad NS in VICAR\n");
600 	returnp=TRUE;
601       }
602 
603       if (sscanf(tmp, " NS = %d",&w) != 1) {
604 	SetISTR(ISTR_WARNING,"LoadPDS: bad scan NS in VICAR\n");
605 	returnp=TRUE;
606       }
607 
608       if ( (tmp=(char *) xv_strstr(scanbuff, " NBB=")))
609         if (sscanf(tmp, " NBB = %d",&lpsize) != 1) {
610 	  SetISTR(ISTR_WARNING,"LoadPDS: bad scan NBB in VICAR\n");
611 	  returnp=TRUE;
612         }
613 
614       vaxbyte = (xv_strstr(scanbuff, " INTFMT='HIGH'") == NULL);
615 
616       if (xv_strstr(scanbuff, " FORMAT='BYTE'"))
617         samplesize = 8;
618       else if (xv_strstr(scanbuff, " FORMAT='HALF'"))
619         samplesize = 16;
620       else if (xv_strstr(scanbuff, " FORMAT=")) {
621 	SetISTR(ISTR_WARNING,"LoadPDS: unsupported FORMAT in VICAR\n");
622 	returnp=TRUE;
623       } else {
624 	SetISTR(ISTR_WARNING,"LoadPDS: bad FORMAT in VICAR\n");
625 	returnp=TRUE;
626       }
627 
628     }
629 
630   } else {
631     SetISTR(ISTR_WARNING,"LoadPDS: Unable to parse data.\n");
632     returnp=TRUE;
633   }
634 
635   /* samplesize can be arbitrarily large (up to int limit) in non-VICAR files */
636   if (samplesize != 8 && samplesize != 16) {
637     SetISTR(ISTR_WARNING,"LoadPDS: %d bits per pixel not supported",
638       samplesize);
639     returnp=TRUE;
640   }
641 
642   if (returnp) {
643     fclose(zf);
644     return 0;
645   }
646 
647   /* PDS files tend to have lots of information like this in them.  The
648    * following are a few of the more interesting headers.  We'd do more, but
649    * there's only so much space in the info box.  I tried to pick a
650    * reasonable subset of these to display; if you want others, it's easy
651    * to change the set.  XV 3.00 has a comment window where we write this
652    * stuff in glorious detail.  Now, if I only had a clue as to what these
653    * fields actually *mean* ... */
654 
655   *infobuff='\0';
656   if (*spacecraft) {
657     strncat(infobuff, spacecraft, sizeof(infobuff) - 1);
658   }
659 
660   if (*target) {
661     strncat(infobuff, ", ", sizeof(infobuff) - strlen(infobuff) - 1);
662     strncat(infobuff, target, sizeof(infobuff) - strlen(infobuff) - 1);
663   }
664 
665   if (*filtname) {
666     strncat(infobuff, ", ", sizeof(infobuff) - strlen(infobuff) - 1);
667     strncat(infobuff, filtname, sizeof(infobuff) - strlen(infobuff) - 1);
668   }
669 
670   if (*itime) {
671     strncat(infobuff, ", ", sizeof(infobuff) - strlen(infobuff) - 1);
672     strncat(infobuff, itime, sizeof(infobuff) - strlen(infobuff) - 1);
673   }
674 
675   SetISTR(ISTR_WARNING, "%s", infobuff);
676 
677   strncpy(pdsuncompfname,fname,sizeof(pdsuncompfname) - 1);
678   ftypstr = "";
679 
680   switch (itype) {
681   case VICAR:
682     sprintf(pinfo->fullInfo,"VICAR, %d bits per pixel. (%ld bytes)",
683       samplesize, filesize);
684     ftypstr = "VICAR";
685     rewind(zf);
686     break;
687 
688   case PDSFIXED:
689   case VIKINGFIXED:
690     sprintf(pinfo->fullInfo,"PDS, %d bits per pixel. (%ld bytes)",
691       samplesize, filesize);
692     ftypstr = "PDS";
693     rewind(zf);
694     break;
695 
696   case PDSVARIABLE:
697   case VIKINGVARIABLE:
698     sprintf(pinfo->fullInfo,
699 	    "PDS, %d bits per pixel, Huffman-encoded. (%ld bytes)",
700                samplesize, filesize);
701     ftypstr = "PDS (Huffman)";
702     fclose(zf);
703 
704 #ifndef VMS
705     snprintf(pdsuncompfname, sizeof(pdsuncompfname) - 1, "%s/xvhuffXXXXXX", tmpdir);
706 #else
707     strcpy(pdsuncompfname,"sys$disk:[]xvhuffXXXXXX");
708 #endif
709 
710 #ifdef USE_MKSTEMP
711     close(mkstemp(pdsuncompfname));
712 #else
713     mktemp(pdsuncompfname);
714     tmpfd = open(pdsuncompfname,O_WRONLY|O_CREAT|O_EXCL,S_IRWUSR);
715     if (tmpfd < 0) {
716       SetISTR(ISTR_WARNING,"Unable to create temporary file.");
717       return 0;
718     }
719     close(tmpfd);
720 #endif
721 
722 #ifndef VMS
723     sprintf(scanbuff,"%s '%s' - 4 > %s", PDSUNCOMP, fname, pdsuncompfname);
724 #else
725     sprintf(scanbuff,"%s %s %s 4",PDSUNCOMP,fname,pdsuncompfname);
726 #endif
727 
728     SetISTR(ISTR_INFO,"De-Huffmanizing '%s'...",fname);
729 
730     /* pdsuncomp filters to a raw file */
731 #ifndef VMS
732     if ( (tempnum=system(scanbuff)) ) {
733       SetISTR(ISTR_WARNING,"Unable to de-Huffmanize '%s'.",fname);
734       return 0;
735     }
736 #else
737     if ( (tempnum = !system(scanbuff)) ) {
738       SetISTR(ISTR_WARNING,"Unable to de-Huffmanize '%s'.",fname);
739       return 0;
740     }
741 #endif
742 
743 
744     zf = xv_fopen(pdsuncompfname,"r");
745     if (!zf) {
746       SetISTR(ISTR_WARNING,"LoadPDS: can't open uncompressed file %s\n",
747 	      pdsuncompfname);
748       return 0;
749     }
750   }
751 
752   /* skip headers */
753 
754   if ( isfixed == TRUE ) {
755     fread(scanbuff, (size_t) labelsize, (size_t) 1, zf);
756   }
757 
758   /* samplesize is bits per pixel; guaranteed at this point to be 8 or 16 */
759   bytewidth = w * (samplesize/8);
760   bufsize = bytewidth * h;
761   if (w <= 0 || h <= 0 || bytewidth/w != (samplesize/8) ||
762       bufsize/bytewidth != h)
763   {
764     SetISTR(ISTR_WARNING,"LoadPDS: image dimensions out of range (%dx%dx%d)",
765       w, h, samplesize/8);
766     fclose(zf);
767     return 0;
768   }
769 
770   image = (byte *) malloc((size_t) bufsize);
771   if (image == NULL) {
772     fclose(zf);
773     if (isfixed == FALSE)
774       unlink(pdsuncompfname);
775     FatalError("LoadPDS: can't malloc image buffer");
776   }
777 
778   if ((lssize || lpsize) &&
779        ((itype == PDSFIXED) || (itype == VIKINGFIXED) || (itype == VICAR)) ) {
780     /* ARrrrgh.  Some of these images have crud intermixed with the image, */
781     /* preventing us from freading in one fell swoop */
782     /* (whatever a fell swoop is) */
783 
784     for (yy=0; yy<h; yy++) {
785       if (lpsize &&
786 	  (teco=fread(scanbuff,(size_t) lpsize,(size_t) 1,zf)) != 1) {
787 	SetISTR(ISTR_WARNING, "LoadPDS: unexpected EOF reading prefix");
788 	fclose(zf);
789 	return 0;
790       }
791 
792       teco = fread(image+(yy*bytewidth), (size_t) bytewidth, (size_t) 1,zf);
793       if (teco != 1) {
794 	SetISTR(ISTR_WARNING, "LoadPDS: unexpected EOF reading line %d",yy);
795 	fclose(zf);
796 	return 0;
797       }
798 
799       if (lssize &&
800 	  (teco=fread(scanbuff,(size_t) lssize,(size_t) 1,zf)) != 1) {
801 	SetISTR(ISTR_WARNING, "LoadPDS: unexpected EOF reading suffix");
802 	fclose(zf);
803 	return 0;
804       }
805     }
806 
807   } else if ((yy=fread(image, (size_t) bytewidth*h, (size_t) 1, zf)) != 1) {
808     SetISTR(ISTR_WARNING,"LoadPDS: error reading image data");
809     fclose(zf);
810     if (itype==PDSVARIABLE || itype==VIKINGVARIABLE)
811       unlink(pdsuncompfname);
812     return 0;
813   }
814 
815   fclose(zf);
816 
817 
818   if (isfixed == FALSE)
819     unlink(pdsuncompfname);
820 
821   pinfo->pic = image;
822   pinfo->w   = w;   /* true pixel-width now (no longer bytewidth!) */
823   pinfo->h   = h;
824 
825   if (samplesize == 16)
826      if (Convert16BitImage(fname, pinfo,
827 	  vaxbyte ^ (*(char *)&samplesize == 16)) == 0)
828         return 0;
829 
830   pinfo->frmType = -1;   /* can't save as PDS */
831   pinfo->colType = F_GREYSCALE;
832   sprintf(pinfo->shrtInfo, "%dx%d %s. ", pinfo->w, pinfo->h, ftypstr);
833 
834   pinfo->comment = (char *) malloc((size_t) 2000);
835   if (pinfo->comment) {
836     char tmp[256];
837     *(pinfo->comment) = '\0';
838 
839     sprintf(tmp, "Spacecraft: %-28.28sTarget: %-32.32s\n", spacecraft, target);
840     strncat(pinfo->comment, tmp, 2000 - strlen(pinfo->comment) - 1);
841 
842     sprintf(tmp, "Filter: %-32.32sMission phase: %-24.24s\n", filtname, mphase);
843     strncat(pinfo->comment, tmp, 2000 - strlen(pinfo->comment) - 1);
844 
845     sprintf(tmp, "Image time: %-28.28sGain mode: %-29.29s\n", itime, gainmode);
846     strncat(pinfo->comment, tmp, 2000 - strlen(pinfo->comment) - 1);
847 
848     sprintf(tmp, "Edit mode: %-29.29sScan mode: %-29.29s\n", editmode, scanmode);
849     strncat(pinfo->comment, tmp, 2000 - strlen(pinfo->comment) - 1);
850 
851     sprintf(tmp, "Exposure: %-30.30sShutter mode: %-25.25s\n", exposure,shuttermode);
852     strncat(pinfo->comment, tmp, 2000 - strlen(pinfo->comment) - 1);
853 
854     sprintf(tmp, "Instrument: %-28.28sImage time: %-28.28s\n", iname, itime);
855     strncat(pinfo->comment, tmp, 2000 - strlen(pinfo->comment) - 1);
856 
857     sprintf(tmp, "Image Note: %-28.28s", inote);
858     strncat(pinfo->comment, tmp, 2000 - strlen(pinfo->comment) - 1);
859   }
860 
861   if (LoadPDSPalette(fname, pinfo))  return 1;
862 
863   /* these are grayscale, so cobble up a ramped colormap */
864   /* Viking images on the CD's seem to be inverted.  Sigh */
865 
866   switch (itype) {
867   case PDSVARIABLE:
868   case PDSFIXED:
869   case VICAR:
870     for (yy=0; yy<=255; yy++) {
871       pinfo->r[yy] = pinfo->g[yy] = pinfo->b[yy] = yy;
872     }
873     break;
874 
875   case VIKINGFIXED:
876   case VIKINGVARIABLE:
877     for (yy=0; yy<=255; yy++) {
878       pinfo->r[yy] = pinfo->g[yy] = pinfo->b[yy] = (255-yy);
879     }
880   }
881 
882   pinfo->normw = pinfo->w;   pinfo->normh = pinfo->h;
883   return 1;
884 }
885 
886 
887 
888 /*
889  *   16-bit to 8-bit conversion
890  */
891 
892 /*******************************************/
Convert16BitImage(fname,pinfo,swab)893 static int Convert16BitImage(fname, pinfo, swab)
894      char *fname;
895      PICINFO *pinfo;
896      int swab;
897 {
898   unsigned short *pShort;
899   long i, j, k, l, n, *hist, nTot;
900   size_t m;
901   byte *lut, *pPix8;
902   FILE *fp;
903   char  name[1024], *c;
904 
905   m = 65536 * sizeof(byte);
906   lut = (byte *) malloc(m);
907   if (lut == NULL) {
908     FatalError("LoadPDS: can't malloc LUT buffer");
909   }
910 
911   /* allocate histogram table */
912   m = 65536 * sizeof(long);
913   hist = (long *) malloc(m);
914   if (hist == NULL) {
915     free(lut);
916     FatalError("LoadPDS: can't malloc histogram buffer");
917   }
918 
919   /* check whether histogram file exists */
920 #ifdef VMS
921   c = (char *) rindex(strcpy(name,
922 			     (c = (char *) rindex(fname, ':')) ? c+1 : fname),
923 		      ']');
924 #else
925   c = (char *) rindex(strcpy(name, fname), '/');
926 #endif /* VMS */
927   (void)strcpy(c ? c+1 : name, "hist.tab");
928 
929   /* read the histogram file which is always LSB_INTEGER */
930   if ((fp = xv_fopen(name, "r")) != NULL) {
931     for (n = k = nTot = 0; n < 65536 && k != EOF; n++, nTot += j) {
932       for (i = j = 0; i < 4 && (k = getc(fp)) != EOF; i++)
933         j = ((j >> 8) & 0xffffff) | ((k & 255) << 24);
934       hist[swab ? ((n & 255) << 8) | ((n >> 8) & 255) : n] = j;
935     }
936     fclose(fp);
937     if (k == EOF)
938       fp = NULL;
939   }
940 
941   /* if no external histogram, make one */
942   if (fp == NULL) {
943     for (n = 0; n < 65536; n++)
944       hist[n] = 0;
945     nTot = n = pinfo->h * pinfo->w;
946     pShort = (unsigned short *)pinfo->pic;
947     while (--n >= 0) {
948       hist[*pShort++]++;
949     }
950   }
951 
952   /* use either histogram equalization or linear stretch */
953   if (0) {      /* was: (conv24MB.flags[CONV24_HIST]) */
954 
955     /* construct lookup table to invert the histogram */
956     m = l = hist[0];
957     i = (nTot - m) >> 8;
958     for (n = j = 1; n < 65536; n++) {
959       k = swab ? ((n & 255) << 8) | ((n >> 8) & 255) : n;
960       if ((l += hist[k]) > m && j < 255)
961         j++, m += i;
962       lut[k] = j;
963     }
964 
965   } else {
966 
967     /* find min and max of histogram */
968     n = nTot/10000;
969     for (m = 0, j = 1; j < 65536; j++)
970       if ((m += hist[swab ? ((j & 255) << 8) | ((j >> 8) & 255) : j]) > n)
971         break;
972     for (m = 0, k = 65535; --k > 1; )
973       if ((m += hist[swab ? ((k & 255) << 8) | ((k >> 8) & 255) : k]) > n)
974         break;
975 
976     /* construct stretch table */
977     for (n = 0; n < 65536; n++) {
978       i = swab ? (((n & 255) << 8) | ((n >> 8) & 255)) : n;
979       m = 1 + (254 * (n - j)) / (k - j);
980       lut[i] = n < j ? 0 : (n > k ? 255 : m);
981     }
982   }
983 
984   free(hist);
985 
986   /* allocate new 8-bit image */
987   n = pinfo->w * pinfo->h;
988   if (pinfo->w <= 0 || pinfo->h <= 0 || n/pinfo->w != pinfo->h) {
989     SetISTR(ISTR_WARNING,"LoadPDS: image dimensions out of range (%dx%d)",
990       pinfo->w, pinfo->h);
991     free(lut);
992     return 0;
993   }
994   pPix8 = (byte *)malloc(n*sizeof(byte));
995   if (pPix8 == NULL) {
996     free(lut);
997     FatalError("LoadPDS: can't malloc 16-to-8-bit conversion buffer");
998   }
999 
1000   /* convert the 16-bit image to 8-bit */
1001   lut[0] = 0;
1002   free(pShort = (unsigned short *)pinfo->pic);
1003   pinfo->pic = pPix8;
1004   while(--n >= 0)
1005     *pPix8++ = lut[*pShort++];
1006   free(lut);
1007   return 1;
1008 }
1009 
1010 /*
1011  *   If there is a PALETTE.TAB file in the same directory, use it
1012  */
1013 
1014 /*******************************************/
LoadPDSPalette(fname,pinfo)1015 static int LoadPDSPalette(fname, pinfo)
1016      char    *fname;
1017      PICINFO *pinfo;
1018 {
1019   FILE    *fp;
1020   char    name[1024], buf[256], *c;
1021   int     i, n, r, g, b;
1022 
1023 #ifdef VMS
1024   c = (char *) rindex(strcpy(name,
1025 			     (c = (char *) rindex(fname, ':')) ? c+1 : fname),
1026 		      ']');
1027 #else
1028   c = (char *) rindex(strcpy(name, fname), '/');
1029 #endif /* VMS */
1030   (void)strcpy(c ? c+1 : name, "palette.tab");
1031 
1032   if ((fp = xv_fopen(name, "r")) == NULL)
1033     return 0;
1034   for (i = 0; i < 256; i++) {
1035     if (fgets(c = buf, (int) (sizeof(buf)-1), fp) == NULL)
1036       break;
1037     while (*c && isspace(*c))
1038       c++;
1039     if (*c == '\0' || *c == '#')
1040       continue;
1041     if (sscanf(buf, "%d %d %d %d", &n, &r, &g, &b) != 4)
1042       continue;
1043     if (n < 0 || n > 255)
1044       continue;
1045     pinfo->r[n] = r;
1046     pinfo->g[n] = g;
1047     pinfo->b[n] = b;
1048     if (r != g && r != b)
1049       pinfo->colType = F_FULLCOLOR;
1050   }
1051   (void)fclose(fp);
1052   return 1;
1053 }
1054 
1055 
1056 #endif /* HAVE_PDS */
1057